消息队列
基于 List 实现
- 生产者调用
LPUSH
往 list 存放消息,左插入,每次的消息都放在队列头 - 消费者调用
BRPOP
到 list 取消息,右弹出,弹出并返回队列末尾的一条消息- 每次取的消息都是队列末尾的一条,如果队列有多条消息,要
BRPOP
多次 BPROP
会阻塞获取消息,如果队列没有消息就等着,可以指定超时时间
- 每次取的消息都是队列末尾的一条,如果队列有多条消息,要
# 生产者,发送一条消息(就是往 list 存入一个数据)
redis> lpush mylist 1
1
redis>
# 消费者,阻塞式获取(第二个参数是超时时间,如果没有消息会阻塞 5 秒)
redis> brpop mylist 5
1) mylist
2) 1
redis>
优点:
- 实现起来非常简单
- 因为 list 是一种数据类型,所以消息也能持久化
- 可以满足消息有序消费
缺点:
- 只支持单节点消费
- 无法避免消息丢失(java 调用 rpop 拿到了消息后,java 服务挂了)
PubSub
- publish 发布,subscribe 订阅,Redis 2.0 开始支持
- 消费者可以订阅一个或多个 channel,生产者向 channel 发消息后,所有的订阅者都能接收到消息
常用命令
命令 | 作用 | 描述 |
---|---|---|
SUBSCRIBE channel [channel] | 订阅一个或多个频道 | |
PUBLISH channel msg | 向指定的频道发消息 | |
PSUBSCRIBE pattern [pattern] | 也是订阅频道,模糊匹配 | PSUBSCRIBE h?ello 问号表示任意单个字符PSUBSCRIBE h*ello 星号表示不限个数的任意字符PSUBSCRIBE h[ae]llo 中括号表示里面其中的一个字符 |
优点:
- 采用发布订阅模式,支持多生产者,多消费者
缺点:
- 不支持数据持久化
- 无法避免消息丢失
- 如果生产者向一个频道发布消息,此时如果没有任何消费者订阅这个频道,这个消息就会丢失
- PubSub 模式下,必须保证有消费者在线,生产这发的消息才会被消费到
- 消息堆积有上限,超出上限的消息会丢失
- 频道不会存放消息,只是起一个连接作用,生产者发一个消息,消费者就一定收到一个消息
- 消费者会把接收到的消息缓存起来,如果处理消息速度过慢,缓存的消息逐渐过多,达到上限时会丢弃消息
Stream
Redis 5.0 引入的一种数据新的数据类型(意味着可以持久化),可以实现一个功能比较完善的消息队列
发送消息
# 命令完整参数
XADD key [NOMKSTREAM] [<MAXLEN | MINID> [= | ~] threshold
[LIMIT count]] <* | id> field value [field value ...]
# eg:向 users 队列发送 {name = Rose, age = 22}
XADD users * name Rose age 22
- [NOMKSTREAM]:如果队列不存在是否自动创建,默认会自动创建
- [<MAXLEN | MINID> [= | ~] threshold [LIMIT count]] :阈值,表明当前队列最多存放的消息数量,当消息达到阈值时存活时间最长的消息将被丢弃
- <* | id>:必须参数,* 和 id 二选一;* 表示 Redis 自动生成(时间戳-自增数字),id 表示用户自己指定
- field value [field value ...]:消息体,键值对的形式
读消息
-
消息读过不会删除,还存在队列中,所以读消息的时候如果想要读取最新的消息,要使用 $ 和具体消息id
-
需要知道阻塞和不阻塞这两种读取方式,如果是非阻塞读,用 $ 是读不到消息的!因为如果不阻塞读取,表示没有消费者处于等待监听消息队列。生产者发送了消息,因为没有消费者监听队列,所以这个消息不会被任何消费者接收到
-
如果阻塞方式读取消息成功,但是处理消息很耗时,处理消息的过程中生产者又发了多条消息,当消费者处理结束重新再次阻塞读取消息时,有可能消息漏读,因为每次读取都要指定本次读取的消息数量,消费者根本不知道在处理的过程中生产者发了多少条消息
# 命令完整参数
XREAD [COUNT count] [BLOCK milliseconds] STREAMS key [key ...] id [id ...]
# eg:读取 user 队列中第一条消息,会返回队列名、消息ID、消息体
XREAD COUNT 1 STREAMS users 0
# eg:阻塞读取消息,如果没有消息就一直阻塞
XREAD COUNT 1 BLOCK 0 STREAMS users $
消息总数
语法比较简单,但是要注意读过的消息不会被删除
XLEN key
# 向 s1 发送一条消息 {k1=v1},返回的是消息ID
49.232.247.70:0>XADD s1 * k1 v1
"1726998044214-0"
# 查看队列消息条数
49.232.247.70:0>XLEN s1
"1"
# 从 0 开始读 s1 队列的消息,读 1 条
49.232.247.70:0>XREAD COUNT 1 STREAMS s1 0
1) 1) "s1"
2) 1) 1) "1726998044214-0"
2) 1) "k1"
2) "v1"
# 再次查看 s1 中多少条消息
49.232.247.70:0>XLEN s1
"1"
消费者组
- 消息分流:消费者加入消费者组,如果一个组中多个消费者,消息只会被组内的一个消费者消费,如果就是想要多个消费者这消费,可以创建多个消费者组(RocketMQ 和这个做法比较一致)
- 消息标识:消费者组会维护一个标识,哪怕消费者宕机,下次重新启动会再次根据标识获取消息,确保每个消息都被消费
- 确认机制:每个消费者有一个 pending list,消费者获取到消息后都会放入 pending list 中,当消费者处理完消息后,需要发送一个 XACK 来确认消息被消费,这时消息从 pendling list 中移除
常用命令
-
创建消费者组
XGROUP CREATE key group <id | $> [MKSTREAM]
-
key:这个消费者组要监听的队列名称
-
group:消费者组名称
-
<id | $>:具体消息id 或 0 或 $ 三选一
- id:大于这个id的消息开始读;0:从第一个开始读;$ 从最新的未读的开始读
- 创建消费者组的时候如果队列已有很多消息了,这些消息又不想消费,就用 $,如果想消费就可以用 0
- 如果消费者组创建时队列没有消息,这时 0 和 $ 就没区别了
-
[MKSTREAM]:当队列不存在是否自动创建队列,默认会创建
-
-
删除消费者组
XGROUP DESTORY key group
-
删除消费者组中的消费者
XGROUP DELCONSUMER key group consumer
-
创建消费者
Redis6 新增的,这个很少会用到,因为在读取消息的时候要指定消费者,如果消费者不存在可以自动创建
XGROUP CREATECONSUMER key group consumer
-
给消费者组添加消费者
Redis6 新增的,这个也很少会用到,也是因为在读取消息的时候就可以指定消费者组里的消费者
XGROUP CREATECONSUMER key group consumer
-
读取消息
XREADGROUP GROUP group consumer [COUNT count] [BLOCK milliseconds] [NOACK] STREAMS key [key ...] id [id ...]
- group:消费者组名称,如果组不存在会自动创建
- consumer:消费者名称,如果消费者不存在会自动创建消费者
- [COUNT count]:本次查询的最大数量
- [BLOCK milliseconds]:是否阻塞读取,可以跟一个阻塞的毫秒时间
- [NOACK]:如果写了表示消息不需要确认,也不会进 pending list
- STREAMS key [key ...]:监听哪个消息队列,可以多个
- id [id ...]:起始id,这里的id和上面的不一样
- > :如果给的是大于符号,从下一个未消费的消息开始读
- 数字:如果不是大于符号,这个id就只能是一个数字,表示下标,将会读取消费者 pending list[下标] 的消息(已消费但未确认的消息)
-
确认消息
确认消息和消费者无关,和消费者组相关
XACK key group id [id ...]
-
读取 pending list
XPENDING key group [[IDLE min-idle-time] start end count [consumer]]
- key:队列名称
- group:消费者组名称
- [IDLE min-idle-time]:在 pending list 存活的时间(如果业务上觉得超过10秒的才算异常,这里可以自定义时间,而不是所有的都算异常)
- start end:开始和结束下标,pending list 中可能会有多条,如果是
- +
表示所有范围(注意这里只是确定范围) - count:读取多少条
- [consumer]:消费者,这是可选参数,如果不指定表示读取消费者组下的所有消费者的 pending list
# 向队列 myqueue 发送 4 条消息 redis> XADD myqueue * k1 v1 1727187879623-0 redis> XADD myqueue * k2 v2 1727187887663-0 redis> XADD myqueue * k3 v3 1727187891673-0 redis> XADD myqueue * k4 v4 1727187896144-0 # 创建一个消费者组,这个组监听的队列是 myqueue,这个组读取的消息从第一个开始 redis> XGROUP CREATE myqueue g1 0 OK # 消费者组g1读取消息,同时自动创建消费者c1(消费者组g1下的消费者c1来读消息) redis> XREADGROUP GROUP g1 c1 COUNT 1 BLOCK 2000 STREAMS myqueue > 1) 1) myqueue 2) 1) 1) 1727187879623-0 2) 1) k1 2) v1 # 再都一条 redis> XREADGROUP GROUP g1 c1 COUNT 1 BLOCK 2000 STREAMS myqueue > 1) 1) myqueue 2) 1) 1) 1727187887663-0 2) 1) k2 2) v2 # 再创建一个消费者c2来读(这次读取的是 k3,因为 k1,k2 被这个组下别的消费者c1读取了) redis> XREADGROUP GROUP g1 c2 COUNT 1 BLOCK 2000 STREAMS myqueue > 1) 1) myqueue 2) 1) 1) 1727187891673-0 2) 1) k3 2) v3 # 确认消息(c1消费了2条消息,c2消费了1条消息) redis> XACK myqueue g1 1727187879623-0 1727187887663-0 1727187891673-0 # 读取队列 myqueue 和 g1 这个组下所有消费者的 pending list,不限范围,读 10 条 XPENDING myqueue g1 - + 10 # 读取消费者 c1 中 pending list 的消息,从第一条开始,读一条 XREADGROUP GROUP g1 c1 COUNT 1 BLOCK 2000 STREAMS myqueue 0
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具