发布订阅(pub/sub)

Redis发布订阅(pub/sub)是一种消息通信模式:发送者(pub)发送消息,订阅者(sub)接收消息。Redis 客户端可以订阅任意数量的频道。

"发布/订阅"模式包含两种角色,分别是发布者和订阅者。发布者可以向指定的频道(channel)发送消息;订阅者可以订阅一个或者多个频道(channel),所有订阅此频道的订阅者都会收到此消息。


实例演示

演示了发布订阅是如何工作的。在我们实例中我们创建了订阅频道名为 redisChat

第一步:打开一个 redis 客户端,订阅频道 redisChat

redis> SUBSCRIBE redisChat 
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "redisChat"
3) (integer) 1

第二步:另外再打开一个 redis 客户端,在频道 redisChat 上发布消息

redis> PUBLISH redisChat "Redis is a great caching technique"
(integer) 1

redis> PUBLISH redisChat "Learn redis by w3cschool.cc" 
(integer) 1

第三步:查看第一次打开的 redis 客户端,会显示接受到的消息

1) "message"
2) "redisChat"
3) "Redis is a great caching technique"

1) "message"
2) "redisChat"
3) "Learn redis by w3cschool.cc" 



发布订阅模式详解

Redis发布/订阅(Pub/Sub)是一种通信机制,将数据推到某个信息管道中,其他客户端可通过订阅这些管道来获取推送信息,以此用于消息的传输。

由三部分组成:发布者(Publisher)、频道(Channel)、订阅者(Subscriber)。

发布者发布的消息分到不同的频道,不需要知道什么样的订阅者者订阅。订阅者对一个或多个频道感兴趣,只需要接收感兴趣的消息,不需要知道什么样的发布者发布。主要目的是解除消息的发布者与订阅者之间的耦合关系。发布者和订阅者都是Redis客户端,频道则是服务器端。

原理

Redis通过 SUBSCRIBE PSUBSCRIBE UNSUBSCRIBE PUNSUBSCRIBE 等命令实现发布和订阅功能。

在Redis底层结构中,客户端和频道的订阅关系是通过一个字典加链表的结构保存的,如下图:

在 Redis 的底层结构中,redis服务器中定义了一个 pubsub_channels 字典,用于保存所有频道的订阅关系,在这个字典中,key 为所有频道名称,value 结构是一个链表,其中存放的是所有订阅这个频道的订阅者客户端。subscribe 命令的实质即为在key中添加value的订阅链。若频道首次被订阅说明在字典中并不存在该渠道的信息,那么程序首先要新建一个对应的 key,并且要赋值一个空链表,然后将对应的客户端加入到链表中。此时链表只有一个元素。若该渠道已经被其他客户端订阅过:这个时候就直接找到key值对应的value客户端信息添加到链表的末尾即可。

推送的消息格式

所有订阅接收的消息均为由三个元素组成的多块响应。

第一个元素是消息类型,有三种类型:

  • subscribe :该类型表示成功订阅到频道响应,此时第二个元素为订阅的频道名称,第三个元素为已订阅的频道数量,例:
    redis> SUBSCRIBE myChannel
    Reading messages... (press Ctrl-C to quit)
    1) "subscribe"
    2) "myChannel"
    3) (integer) 1
    
  • unsubscribe :该类型表示成功取消订阅到的频道响应,此时第二个元素为订阅的频道名称,第三个元素为已订阅的频道数量,例:
    redis> UNSUBSCRIBE myChannel
    1) "unsubscribe" 
    2) "myChannel"  
    3) (integer) 0
    
  • message :该类型表示订阅者客户端接收到其他客户端发出的发布命令结果,此时第二个元素表示来源频道的名称,第三个元素是实际的消息内容,例:
    redis> SUBSCRIBE myChannel myChannel2
    1) "subscribe" 
    2) "myChannel"  
    3) (integer) 1
    1) "subscribe"
    2) "myChannel2"
    3) (integer) 2
    1) "message"
    2) "myChannel1"
    


发布订阅命令

发送消息

PUBLISH 命令为发送信息,返回值为接收到该消息的订阅者数量。

redis> PUBLISH mychannel "Hello"
(integer) 1

redis> PUBLISH mychannel "World"
(integer) 1

订阅频道

SUBSCRIBE 命令为订阅频道,返回值消息格式如上所述。

redis> SUBSCRIBE myChannel
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "myChannel"
3) (integer) 1
1) "message"
2) "myChannel"
3) "Hello"
1) "message"
2) "myChannel"

模式匹配

PSUBSCRIBE 命令订阅一个或多个符合给定模式的频道。每个模式以 * 作为匹配符,例如 news.*匹配所有以 news.开头的频道(news.one、news.global.today 等等)。

redis> PSUBSCRIBE new.*
1) "psubscribe"
2) "new.*"
3) (integer) 1
1) "pmessage"
2) "new.*"      # 消息匹配的模式
3) "new.one"    # 消息本身的目标频道

注:当订阅客户端同时订阅某种模式和符合该模式的具体某个频道时,那么会接收到发布者推送的信息两次,两次接收的信息格式不同,一个为 message 类型,另一个为 pmessage 类型,消息内容一致。

取消订阅

UNSUBSCRIBE 命令为取消订阅频道,与订阅频道命令相同,也有对应匹配模式 PUNSUBSCRIBE

如果没有填写指定频道,即一个无参数的 UNSUBSCRIBE 被调用执行,那么该客户端订阅的所有频道都会被退订。

由于Redis的订阅操作是阻塞式的,因此一旦客户端订阅了某个频道或模式,就将会一直处于订阅状态直到退出。在 SUBSCRIBE PSUBSCRIBE UNSUBSCRIBE PUNSUBSCRIBE 命令中,其返回值都包含了该客户端当前订阅的频道和模式的数量,当这个数量变为0时,该客户端会自动退出订阅状态。