使用Reids实现简单消息队列

队列操作

简单队列

利用List数据结构可以实现简单的队列,在于使用List提供插入和移除api来完成简单队列操作;

准备数据

获取数据

后入先出
使用redis提供的apiLPOP可以从队列左边获取数据,也就是从队列的头部获取到最后插入的数据,也可以使用BLPOP设置超时时间,从而做到等待数据写入;

先入先出
与上面的一样,只是把获取数据的方向改成另一边,使用RPOP可以从队列右边获取数据,也就是从队列的尾部获取到最先插入的数据,也可以使用BRPOP设置超时时间,从而做到等待数据写入;

等待
使用BLPOPBRPOP可以移除并获取列表头部/尾部获取第一个元素;如果把BLPOP命令的超时时间设置为0,表示一直阻塞,直到数据写入;

实现

到这就可以将List当成一个简单的队列,推送数据的生产者,将数据写入到List,然后消费者等着List中有数据,然后获取;

生产者

using StackExchange.Redis;

var connection =  ConnectionMultiplexer.Connect("localhost");
var redis = connection.GetDatabase(1);

foreach (var i in Enumerable.Range(0,10))
{
    await redis.ListLeftPushAsync("data",i);
    Console.WriteLine($"写入 {i}");
    Thread.Sleep(1000);
}

消费者

using StackExchange.Redis;

var connection = ConnectionMultiplexer.Connect("localhost");
var redis = connection.GetDatabase(1);

while (true)
{
    var res = await redis.ListRightPopAsync("data");

    if (!string.IsNullOrEmpty(res))
    {
        Console.WriteLine($"读取 {res}");
    }

    Thread.Sleep(1000);
}

至此,消息已经消费,但是消息的多播并不能实现,队列中的消息被移除了,另外的消费者就拿不到这条消息,同时ListRightPopAsync方法对应的redisRPOP命令,StackExchange.Redis中不支持Redis中对应的BLPOPBRPOP命令,但是可以使用发布订阅模式实现;

发布订阅

生产者

using StackExchange.Redis;

var connection = ConnectionMultiplexer.Connect("localhost");
var sub = connection.GetSubscriber();

foreach (var i in Enumerable.Range(0,10))
{
    await sub.PublishAsync("channel", $"消息 {i}");
    Console.WriteLine($"发布消息 {i}");
    Thread.Sleep(1000);
}

Console.ReadLine();

消费者

using StackExchange.Redis;

var connection = ConnectionMultiplexer.Connect("localhost");
var sub = connection.GetSubscriber();

sub.Subscribe("channel",
            (channel, message) =>
            {
                Console.WriteLine($"接收消息:{message}");
            });

Console.ReadLine();

通过消息生产者将消息发布到某个channel,同时消费者订阅该channel,即可获取到消息;当有多个消费者也可以接受到消息了,如果在消费者订阅之前消息就已消费则不能再获取到该消息;

开启两个消费者,都订阅了同一channel,利用Sleep使消费者2晚一秒订阅到channel,导致没有接收到消息0;

如果想要做到消息的持久化可以同时将Redis的List利用起来,或者使用其他介质存一份,在消息发布的同时使用将消息推到List中,然后在消费者中去将消息移除,历史消息可以在订阅channel前获取到当前List中消息,开启一个线程去执行业务操作并移除当前消息;

生产者

using StackExchange.Redis;

var connection = ConnectionMultiplexer.Connect("localhost");
var redis = connection.GetDatabase(1);
var sub = connection.GetSubscriber();

foreach (var i in Enumerable.Range(0,100))
{
    await redis.ListLeftPushAsync("historyMsg", $"消息 {i}");
    await sub.PublishAsync("channel", $"消息 {i}");
    Console.WriteLine($"发布消息 {i}");
    Thread.Sleep(1000);
}

Console.ReadLine();

消费者

using StackExchange.Redis;

var connection = ConnectionMultiplexer.Connect("localhost");
var redis = connection.GetDatabase(1);
var sub = connection.GetSubscriber();

//这里可以选择不同消息持久化介质
var count = (await redis.ListRangeAsync("historyMsg")).Length;

if (count > 0)
{
    await Task.Run(() =>
    {
        for (int i = 0; i < count; i++)
        {
            string result = redis.ListRightPop("historyMsg");
            Console.WriteLine("处理历史消息:" + result);
        }
    });
}

sub.Subscribe("channel",
            (channel, message) =>
            {
                redis.ListRightPop("historyMsg");
                Console.WriteLine($"接收消息:{message}");
            });

Console.ReadLine();

posted @ 2023-02-17 17:19  贰拾~  阅读(89)  评论(0编辑  收藏  举报