Redis的发布与订阅

什么是发布与订阅?

发布与订阅(pub/sub)是一种消息通信模式,目的是解耦消息发布者和消息订阅者之间的耦合,redis作为一个发布/订阅的服务器,在发布者和订阅者之间起到了消息路由的功能。Redis中的消息类型叫做通道(channel),一个客户端可以订阅多个通道,也可以向多个通道发送消息。

如下图所示,加入有3个客户端(客户端A、客户端B、客户端C)订阅了频道1,那么当客户端1向频道1发送消息时,3个客户端都会收到消息。

 频道的订阅与退订

命令:subscribe channel [channel ...],客户端执行该命令订阅某个或某些频道。

Redis将所有频道的订阅关系都保存到服务器pubsub_channels字典中,这个字典的键是某个被订阅的频道,值则是一个链表,链表中记录所有订阅这个频道的客户端。如下图所示,客户端A订阅了频道1,客户端B和客户端C订阅了频道2,客户端D、客户端E和客户端F订阅了频道3。

如果此时客户端A退订了频道1,订阅频道2,频道1处于没有客户端订阅状态,这是频道1的订阅关系会从pubsub+channels字典表中删除,客户端A会加到信息会保存到频道2的订阅关系链表中。

举个简单的例子(就使用上一篇文章中的128和129两台服务器)

使用xshell工具,启动在129上的redis服务,然后打开3个窗口,依次记为窗口1、窗口2、窗口3

在窗口2和窗口3中订阅频道,如下所示

窗口2:

127.0.0.1:6379> subscribe channel.1 channel.2
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "channel.1"
3) (integer) 1
1) "subscribe"
2) "channel.2"
3) (integer) 2

窗口3:

127.0.0.1:6379>  subscribe channel.1 channel.3
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "channel.1"
3) (integer) 1
1) "subscribe"
2) "channel.3"
3) (integer) 2

此时,在窗口1中执行发布消息命令,窗口1:

127.0.0.1:6379> publish channel.1 hhhh
(integer) 2
127.0.0.1:6379> publish channel.2 aaaaa
(integer) 1
127.0.0.1:6379> publish channel.3 bbbbb
(integer) 1

窗口2:

127.0.0.1:6379> subscribe channel.1 channel.2
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "channel.1"
3) (integer) 1
1) "subscribe"
2) "channel.2"
3) (integer) 2
1) "message"
2) "channel.1"
3) "hhhh"
1) "message"
2) "channel.2"
3) "aaaaa"

窗口3:

127.0.0.1:6379>  subscribe channel.1 channel.3
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "channel.1"
3) (integer) 1
1) "subscribe"
2) "channel.3"
3) (integer) 2
1) "message"
2) "channel.1"
3) "hhhh"
1) "message"
2) "channel.3"
3) "bbbbb"

当然,你也可以打开128(129的主服务器),在128中打开redis命令窗口,记为窗口4,在窗口4中发布消息,值得注意的是返回值为0,但是129上是可以收到消息的;你反过来测试下回发现,在129上发布消息,128上收不到。个人理解:128是129的主服务器,订阅命令其实质是一个写命令,而主服务器的写命令会同步到从服务器,也就是说其实在129上也会发布消息,这样就能够解释在128上发布的消息,返回值为0,但是129上却能接收到消息了。

窗口4:

127.0.0.1:6379> publish channel.1 ceshiyixia
(integer) 0
127.0.0.1:6379> publish channel.2 biubiubiu
(integer) 0
127.0.0.1:6379> publish channel.3 qqqqqq
(integer) 0

窗口2:

127.0.0.1:6379> subscribe channel.1 channel.2
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "channel.1"
3) (integer) 1
1) "subscribe"
2) "channel.2"
3) (integer) 2
1) "message"
2) "channel.1"
3) "hhhh"
1) "message"
2) "channel.2"
3) "aaaaa"
1) "message"
2) "channel.1"
3) "ceshiyixia"
1) "message"
2) "channel.2"
3) "biubiubiu"

窗口3:

127.0.0.1:6379>  subscribe channel.1 channel.3
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "channel.1"
3) (integer) 1
1) "subscribe"
2) "channel.3"
3) (integer) 2
1) "message"
2) "channel.1"
3) "hhhh"
1) "message"
2) "channel.3"
3) "bbbbb"
1) "message"
2) "channel.1"
3) "ceshiyixia"
1) "message"
2) "channel.3"
3) "qqqqqq"

129上发布消息,128上接收

窗口4:

127.0.0.1:6379> subscribe channel.5 
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "channel.5"
3) (integer) 1

窗口1:

127.0.0.1:6379> publish channel.5 1111
(integer) 0

窗口1发布了消息,但是128上不回接收到消息。

命令:unsubscribe channel [channel ...],客户端执行该命令退订某个频道。

在窗口3中使用ctrl+c退出订阅模式,然后再进入redis命令模式,使用unsubscribe命令进行退订。

^C
[root@localhost src]# ./redis-cli
127.0.0.1:6379> unsubscribe channel1.3
1) "unsubscribe"
2) "channel1.3"
3) (integer) 0

这里存在一个有意思的问题,首先还原下窗口3,如下所示

127.0.0.1:6379> subscribe channel.1 channel.3
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "channel.1"
3) (integer) 1
1) "subscribe"
2) "channel.3"
3) (integer) 2

重新打开一个129的窗口--窗口5,使用pubsub channels命令,查看已经订阅的频道

127.0.0.1:6379> pubsub channels
1) "channel.2"
2) "channel.1"
3) "channel.3"

然后切换到窗口3,使用ctrl+c退出订阅模式

127.0.0.1:6379> subscribe channel.1 channel.3
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "channel.1"
3) (integer) 1
1) "subscribe"
2) "channel.3"
3) (integer) 2
^C
[root@localhost src]# 

再次在窗口5中执行pubsub channels命令,查看已经订阅的频道

127.0.0.1:6379> pubsub channels
1) "channel.2"
2) "channel.1"

然后在窗口3中执行退订命令

^C
[root@localhost src]# ./redis-cli
127.0.0.1:6379> unsubscribe channel1.3
1) "unsubscribe"
2) "channel1.3"
3) (integer) 0

窗口5显示结果

127.0.0.1:6379> pubsub channels
1) "channel.2"
2) "channel.1"

官网上unsubscribe命令是用来退订频道的,所以不需要质疑这条命令的准确性。但是你就会发现,其实对于channel1.3来说,在窗口3中退出订阅时已经关闭了对这个频道的订阅,与退订的命令unsubscribe channel1.3并没有关系。

希望有知道原因的前辈可以把原因写到评论区,非常感谢!

模式的订阅与退订

模式的订阅关系保存到pubsub_patterns属性中,它是一个链表,链表中每一个节点都包含一个PubsubPattern结构,这个结构的pattern属性记录了被订阅的模式,client属性记录了订阅模式的客户端。

PubsubPattern结构:

PubsubPattern

Client

客户端A

Pattern

模式a

它表示的是客户端A订阅模式a。

命令:psubscribe pattern [pattern ...],订阅模式,执行psubscribe命令时,生成一个PubsubPattern结构,并将这个结构添加到pubsub_patterns链表中;

举个例子:

打开128上3个redis的命令窗口,分别记为窗口A、窗口B、窗口C;

窗口B订阅模式name*(以name开头)

127.0.0.1:6379> psubscribe name*
Reading messages... (press Ctrl-C to quit)
1) "psubscribe"
2) "name*"
3) (integer) 1

窗口C订阅模式score*(已score开头)

127.0.0.1:6379>  psubscribe score*
Reading messages... (press Ctrl-C to quit)
1) "psubscribe"
2) "score*"
3) (integer) 1

窗口A中发布消息

127.0.0.1:6379> publish name_su hhh
(integer) 1
127.0.0.1:6379> publish name_zhang zzzz
(integer) 1
127.0.0.1:6379> publish score.su 92
(integer) 1
127.0.0.1:6379> publish score.zhang 85
(integer) 1

此时,窗口B

127.0.0.1:6379> psubscribe name*
Reading messages... (press Ctrl-C to quit)
1) "psubscribe"
2) "name*"
3) (integer) 1
1) "pmessage"
2) "name*"
3) "name_su"
4) "hhh"
1) "pmessage"
2) "name*"
3) "name_zhang"
4) "zzzz"

窗口C

127.0.0.1:6379>  psubscribe score*
Reading messages... (press Ctrl-C to quit)
1) "psubscribe"
2) "score*"
3) (integer) 1
1) "pmessage"
2) "score*"
3) "score.su"
4) "92"
1) "pmessage"
2) "score*"
3) "score.zhang"
4) "85"

命令:punsubscribe [pattern [pattern ...]],退订模式,将对应的PubsubPattern结构从链表中删除。这里不做描述,可以参考下上面频道的退订。

命令:publish channel message,发布消息。这个命令在上面例子中已经使用到了,当redis客户端执行这个命令时,将消息(message)发送到频道(channel),消息会发送给频道的所有订阅者,另外,如果有模式与频道匹配,则消息还会发送给所有的模式订阅者。这个命令的使用可以看上面例子,这里不再做描述。

命令:pubsub subcommand [argument [argument ...]],客户端可以通过这个命令来查看频道或者模式的相关信息。

pubsub channels :查询当前订阅频道;

pubsub numsub :查询任意多个频道的订阅者数量;

pubsub numpat :查询当前被订阅模式的数量。

举个例子,128上打开3个窗口,分别记为a,b,c

窗口b中订阅频道

127.0.0.1:6379> subscribe name.su name.liu 
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "name.su"
3) (integer) 1
1) "subscribe"
2) "name.liu"
3) (integer) 2

窗口c中订阅模式

127.0.0.1:6379> psubscribe name.* score.*
Reading messages... (press Ctrl-C to quit)
1) "psubscribe"
2) "name.*"
3) (integer) 1
1) "psubscribe"
2) "score.*"
3) (integer) 2

窗口a中查看订阅频道

pubsub channels:查看所有订阅频道;pubsub channels pattern:查看所有满足pattern的订阅频道

127.0.0.1:6379> pubsub channels
1) "name.liu"
2) "name.su"
127.0.0.1:6379> pubsub channels name.s*
1) "name.su"

 pubsub numsub :查询任意多个频道的订阅者数量

127.0.0.1:6379> pubsub numsub name.su name.xie name.liu
1) "name.su"
2) (integer) 1
3) "name.xie"
4) (integer) 0
5) "name.liu"
6) (integer) 1

pubsub numpat :查询当前被订阅模式的数量

127.0.0.1:6379> pubsub numpat 
(integer) 2

以上就是redis的发布与订阅。

 

 

如果有写的不对的地方,请大家多多批评指正,非常感谢!

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值