1、简介
1.1 List消息队列
redis
的 List
数据类型结构提供了 blpop 、brpop
命令结合 rpush、lpush
命令可以实现消息队列机制,基于双端链表模式的发布与订阅功能。
存在两个局限性:
- 不能支持一对多的消息分发,只能有一个或多个消费者轮着去消费,却不能将消息同时发给其他消费者。
- 如果生产者生成的速度远远大于消费者消费的速度,易堆积大量未消费的消息
1.2 发布订阅
SUBSCRIBE
,UNSUBSCRIBE
和PUBLISH
实现了发布/订阅消息规范,Redis的发布与订阅功能可以让客户端通过广播方式,将消息message
同时发送给可能存在的多个客户端,并且发送消息的客户端不需要知道接收消息的客户端的具体信息。换句话说,发布消息的客户端与接收消息的客户端两者之间没有直接联系。在Redis中,客户端可以通过订阅特定的频道channel
来接收发送至该频道的消息,把订阅频道的客户端称为订阅者subscriber
。一个频道可以有多个订阅者,而一个订阅者也可订阅任意多个频道。除此之外,客户端还可以通过向频道发送消息的方式,将消息发送给频道的所有订阅者,我们把这些发送消息的客户端称为发布者publisher
。
但是,Redis的Pub/Sub展示了最多一次消息传递语义,即消息最多只会传递一次。一旦消息由Redis服务器发送,就没有再次发送的机会。如果订阅者无法处理消息(例如,由于错误或网络断开连接),则消息将永远丢失。需要更强的交付保证,可以使用 Streams。
发布订阅的实现场景: 一般使用消息中间件来做,kafka、RabbitMQ、ActiveMQ、RocketMQ …等
- 实时沟通消息系统
- 微信公众号(点击关注,后台发送一篇博客,订阅的用户就可以监听到)粉丝关注功能、文章推送
- 电商中,用户下单成功之后向指定频道发送消息,下游业务订阅支付结果这个频道处理自己相关业务逻辑
2、订阅
2.1 基于频道订阅
subscribe channel [channel … ]
:订阅给定的一个或多个频道
$ redis > SUBSCRIBE channel1 channel2
redis > SUBSCRIBE channel1 channel2
Reading messages... (press Ctrl-C to quit)
1) "subscribe" #返回值类型:表示订阅成功!
2) "channel1" #订阅频道的名称
3) (integer) 1 #当前客户端已订阅频道的数量
1) "subscribe"
2) "channel2"
3) (integer) 2
1) "message" #消息表示
2) "channel1"
3) "helloxie"
订阅成功后,会一直阻塞监听来获取订阅通道的信息,客户端不能再发送任何除了SUBSCRIBE、SSUBSCRIBE、PSUBSCRIBE、UNSUBSCRIBE、SUNSUBSCRIBE、PUNSUBSCRIBE、PING、RESET
和QUIT
命令以外的其他命令。Redis 客户端会不断地接收来自 Redis 服务器的发布消息,并且不会处理其他命令。但若使用RESP3(Redis Serialization Protocol),客户端可以在订阅状态下发出任何命令。版本大于6.2.0,可以调用RESET
退出订阅状态。
客户端指的是
jedis、lettuce
的客户端,redis-cli
是无法退出订阅状态的!只能通过Ctrl+c强制退出!
-
unsubscribe channel [channel … ]
:退订给定的频道,若没有指定channel,则默认退订所有频道 -
publish channel message
:将消息发送给指定频道 channel ,返回结果:接收到信息的订阅者数量,无订阅者返回0;也可以使用*
、?
指定多个频道。
redis > PUBLISH channel1 "fristMessage"
(integer) 1 #接受者数量
PUBSUB CHANNELS [pattern]
:查看订阅与发布系统的状态,说明:返回所有活跃频道列表(即至少有一个订阅者的频道)
redis > PUBSUB CHANNELS *
1) "channel1"
PUBSUB NUMSUB [channel [channel ...]]
:返回指定频道的订阅者数量(不包括模式订阅)。若不指定频道,将只返回一个空列表。
redis > pubsub numsub channel1
1) "channel1"
2) (integer) 2
PUBSUB SHARDCHANNELS [pattern]
:7.0.0版本,列出当前活跃的分片频道。活跃的分片频道是指至少有一个订阅者的 Pub/Sub 分片频道。如果没有指定模式,则列出所有频道;如果指定了模式pattren
,则只列出与指定的 glob 样式模式匹配的频道。
redis > PUBSUB SHARDCHANNELS
1) "orders"
redis >PUBSUB SHARDCHANNELS o*
1) "orders
PUBSUB SHARDNUMSUB [shardchannel [shardchannel ...]]
:7.0.0版本,返回指定分片频道的订阅者数量。
redis > PUBSUB SHARDNUMSUB orders
1) "orders"
2) (integer) 1
集群注意事项:
在 Redis 集群中,客户端可以订阅每个节点,并且还可以发布到每个其他节点。集群将确保将所需的发布消息转发出去。尽管如此,集群中
PUBSUB
的响应仅报告节点的 Pub/Sub 上下文中的信息,而不是整个集群的信息。Redis 7.0引入了分片发布订阅(sharded Pub/Sub)。在分片发布订阅中,通过与键分片算法相同的算法,将分片频道分配给槽(slot)。分片消息必须发送到拥有哈希到的分片频道的槽的节点上。集群确保将已发布的分片消息转发到分片中的所有节点,因此客户端可以通过连接到负责槽的主节点或任何副本来订阅分片频道。
SSUBSCRIBE
、SUNSUBSCRIBE
和SPUBLISH
用于实现分片发布订阅。
2.2 基于模式订阅
psubscribe pattern1 [pattern…]
:订阅一个或多个符合给定模式的频道,*
作为通配符、?
表示一个占位符(cn* 匹配所有以cn开头的频道)
redis > psubscribe channel*
Reading messages... (press Ctrl-C to quit)
1) "psubscribe"
2) "channel*"
3) (integer) 1
1) "pmessage" #返回值的类型:信息
2) "channel*" #信息匹配的模式:a?
3) "channel1" #信息本身的目标频道:aa
4) "hello" #信息的内容:"hello world"
punsubscribe [pattern [pattern …] ]
:退订所有给定模式的频道,说明:pattern 未指定,则订阅的所有模式都会被退订,否则只退订指定的订阅的模式。
127.0.0.1:6379> punsubscribe pattern [pattern ...]
# 返回值为当前客户端订阅的频道和模式的数量
PUBSUB NUMPAT
:只统计使用PSUBSCRIBE
命令执行的,返回客户端订阅的唯一模式的数量。
redis > psubscribe a* b? c123
Reading messages... (press Ctrl-C to quit)
1) "psubscribe"
2) "a*"
3) (integer) 1
1) "psubscribe"
2) "b?"
3) (integer) 2
1) "psubscribe"
2) "c123"
3) (integer) 3
redis > PUBSUB NUMPAT
(integer) 3
3、缺点
发布的消息在Redis系统中不能持久化,因此,必须先执行订阅,再等待消息发布。如果先发布了消息,那么该消息由于没有订阅者,消息将被直接丢弃。消息只管发送对于发布者而言消息是即发即失的,不管接收,也没有ACK机制,无法保证消息的消费成功。以上的缺点导致Redis的Pub/Sub
模式就像个小玩具,在生产环境中几乎无用武之地,为此Redis5.0版本新增了Stream
数据结构,不但支持多播,还支持数据持久化,相比Pub/Sub
更加的强大。