Asp.net core中RedisMQ的簡單應用

最近一個外部的項目,使用到了消息隊列,原本是用rabbitmq實現的,可是因爲是部署到別人家的服務器上,想盡可能簡化一些,項目中原本也要接入了redis緩存,就嘗試使用redis來實現簡單的消息隊列。git

使用redis作消息隊列有兩種方法,一種是使用pub/sub,另外一種是使用list結構,配合brpop來消費。這兩種方式各有特色,這裏簡述一下:github

  • pub/sub模式,支持多客戶端消費,可是不支持持久化,這就意味着客戶端斷開的時間內發佈的消息將會所有捨棄掉。
  • list配合brpop,默認不支持多客戶端消費,支持持久化。這種模式的多客戶端消費能夠變相實現,好比下面的僞代碼:
#第一步push消息到隊列
lpush  listA  msg
#第二步,一個專門的分發客戶端取出消息,push到各個子隊列
var msg=brpop listA
lpush listA1 msg
lpush listA2 msg
......
#第三步,多個客戶端從對應的隊列消費消息
var client1_msg= brpop listA1
var client2_msg= brpop listA2
......

消息丟失不太可取,因此我選擇了list ,下一步須要選擇一個合適的客戶端。
Stackexchange.redis 算是一個老牌的客戶端了,可是因爲其採用多路複用的模式,無法支持Redis的blocking pops特性。因此我採用了國人寫的CSRedisCore。redis

首先須要在appsettings.json中添加redis的鏈接字符串:json

{
  "ConnectionStrings": {
    "redis": "{ip}:{port},password=123456,prefix=my_"
  }
}

具體配置請參考github上的文檔:https://github.com/2881099/csredis緩存

而後在startup.cs的ConfigureServices中配置redis:服務器

public void ConfigureServices(IServiceCollection services)
       {
            //redis配置
            RedisHelper.Initialization(new CSRedis.CSRedisClient(Configuration.GetConnectionString("redis")));
       }

固然也能夠採用依賴注入的方式添加CSRedisClient實例,這個不糾結。網絡

在項目中有好幾處使用到了隊列,因此先封裝一個消費服務:併發

public abstract class RedisMQConsumer : BackgroundService
    {
        protected abstract string CacheKey { get; }

        protected ILogger<RedisMQConsumer> logger;

        public RedisMQConsumer(ILogger<RedisMQConsumer> logger)
        {
            this.logger = logger;
        }

        protected override Task ExecuteAsync(CancellationToken stoppingToken)
        {
            return Task.Run( async() =>
            {
                while (!stoppingToken.IsCancellationRequested)
                {
                    try
                    {
                        var msg = RedisHelper.BRPop(5, CacheKey);
                        try
                        {
                            if (string.IsNullOrEmpty(msg)) continue;
                            if (!Process(msg))
                            {
                                //加入錯誤處理隊列,能夠在後臺寫功能手動處理
                                RedisHelper.LPush(CacheKey + "_err", msg);
                            }
                        }
                        catch (Exception exp)
                        {
                            //加入錯誤處理隊列,能夠在後臺寫功能手動處理
                            RedisHelper.LPush(CacheKey + "_err", msg);
                            logger.LogError(exp, "RedisMQConsumer Execute error");
                        }
                    }
                    catch
                    {
                        //網絡可能中斷
                        await Task.Delay(TimeSpan.FromSeconds(5), stoppingToken);
                    }
                        
                }
            }, stoppingToken);
        }

        protected abstract bool Process(string message);
    }

而後就能夠繼承RedisMQConsumer,編寫實際邏輯:app

public class AddOrderMQConsumer : RedisMQConsumer
    {
        public AddOrderMQConsumer(ILogger<RedisMQConsumer> logger) : base(logger)
        {
        }
        protected override string CacheKey => "addOrder";
        protected override bool Process(string message)
        {
            var order = JsonSerializer.Deserialize<Order>(message);
            //處理邏輯
            return true;
        }
    }

發佈消息只是往隊列中添加項:async

RedisHelper.LPush("addOrder", order);

最後把消費服務添加到startup.cs中:

public void ConfigureServices(IServiceCollection services)
        {
            //redis配置
            RedisHelper.Initialization(new CSRedis.CSRedisClient(Configuration.GetConnectionString("redis")));
            
            //redis消息隊列消費服務,放在redis配置下方
            services.AddHostedService<AddOrderListener>();
        }

經測試,還算穩定,小併發項目可使用。

相關文章
相關標籤/搜索