当前位置:首页 > 问答 > 正文

Redis消息队列怎么配置和实现,简单聊聊用Redis做消息传递的那些事儿

说到用Redis做消息传递,其实这事儿挺有意思的,你可能知道Redis是个速度飞快的内存数据库,常被用来做缓存,但它那几种简单的数据结构,拿来搞个轻量级的消息队列,简直是手到擒来,特别适合那些不想引入RabbitMQ、Kafka这种重量级选手的场景。

最直接、最经典的玩法就是用Redis的列表(List)结构,这就像是排队的队伍,遵循“先进先出”的原则,生产者用LPUSH命令把消息从队列左边塞进去,消费者用RPOP命令从队列右边把消息取出来处理,就这么一左一右,一个简单的队列就转起来了,你的网站有个发送邮件的任务,用户注册成功后,Web服务器就作为一个生产者,执行一句 LPUSH email_queue "欢迎新用户xxx注册" ,把这个任务塞进队列,后台专门有个发邮件的程序作为消费者,不停地用 RPOP 从队列里取任务,取到就执行发邮件的操作,这样就把耗时的发邮件任务和用户注册这个动作分开了,用户不用干等着邮件发送成功才看到注册成功的页面,体验就好多了。

Redis消息队列怎么配置和实现,简单聊聊用Redis做消息传递的那些事儿

这里有个小问题,消费者怎么知道队列里来新消息了呢?如果消费者傻傻地用一个循环不停地去RPOP,队列空的时候就会变成“空轮询”,白白消耗CPU资源,为了解决这个尴尬,Redis提供了一个“阻塞”版本的命令,叫BRPOP,消费者可以执行 BRPOP email_queue 30 ,意思是:去email_queue队列里取消息,如果队列是空的,我就等在这,最多等30秒,这30秒内只要有消息进来,我立马拿到并返回;如果30秒后还是没消息,我再返回空然后可以决定是继续等还是干点别的,这样就实现了高效的等待,避免了无用的资源浪费。

Redis消息队列怎么配置和实现,简单聊聊用Redis做消息传递的那些事儿

上面的模式有个缺陷:它一个消息只能被一个消费者处理,如果你想实现的是“发布/订阅”这种模式,也就是一条消息发出去,多个订阅者都能收到,比如聊天室里的群发消息,那列表就不太合适了,Redis提供了专门用于“发布/订阅”(Pub/Sub)的命令,发布者用 PUBLISH channel_name "消息内容" 向一个频道发送消息,而任何消费者都可以用 SUBSCRIBE channel_name 订阅这个频道,一旦有消息发布到这个频道,所有订阅了的消费者都会同时收到这条消息,这个模式很快,但它是“非持久化”的,也就是说,它不存储消息,如果一个消费者在消息发布的那一刻没有订阅频道,或者它刚好断线了,那它就永远错过了这条消息,消息不会为它保留。

为了弥补Pub/Sub不能持久化消息的不足,Redis在5.0版本引入了更强大的数据结构——流(Stream),Stream可以说是Redis为消息队列场景量身定做的功能,它就像是一个只追加的日志文件,每条消息都有一个唯一的ID,生产者用 XADD mystream key1 value1 key2 value2 来添加消息,那个 号让Redis自动生成一个基于时间的唯一ID,消费者则属于某个消费者组,Stream的精妙之处在于,它支持消费者组的概念,可以确保一条消息在组内只会被一个消费者处理(类似于列表的竞争消费),并且消息会被持久化,消费者需要显式地确认消息处理完成(ACK),这样,即使某个消费者中途挂掉,它没有确认的消息还可以被组内其他消费者接手处理,保证了消息不会丢失,这功能就非常接近专业的消息队列了。

总结一下,用Redis做消息传递,主要有这么几种路子,根据你的需求来选:最简单的任务队列,用List和BRPOP;需要广播消息的,用Pub/Sub,但要能接受消息可能丢失;想要功能更完善、更可靠,有消费者组和消息持久化需求的,那就用Stream,来源上,这些方法都来自于Redis官方的文档和命令手册,是经过大量实践验证的成熟方案,Redis提供的这些工具,让它在轻量级消息传递和异步处理领域,成为了一个非常灵活和高效的选择。