Redis的发布与订阅功能由PUBLISH、SUBSCRIBE、PSUB-SCRIBE等命令组成。本章主要介绍这些命令的实现原理。

通过执行SUBSCRIBE命令,客户端可以订阅一个或多个频道,从而成为这些频道的订阅者(subscriber):每当有其他客户端向被订阅的频道发送消息(message)时,频道的所有订阅者都会收到这条消息

假如ABC三个客户端都执行了:

SUBSCRIBE "news.it"

那么这三个客户端都成了”news.it”频道的订阅者,

向”news.it”频道发送消息”hello”,那么”news.it”的三个订阅者都将收到这条消息。

除了订阅频道之外,客户端还可以通过执行PSUBSCRIBE命令订阅一个或多个模式,从而成为这些模式的订阅者:每当有其他客户端向某个频道发送消息时,消息不仅会被发送给这个频道的所有订阅者,它还会被发送给所有与这个频道相匹配的模式的订阅者。

1. 订阅与退订

1.1 频道的订阅与退订

Redis将所有频道的订阅关系都保存在服务器状态的pubsub_channels字典里面,这个字典的键是某个被订阅的频道,而键的值则是一个链表,链表里面记录了所有订阅这个频道的客户端。

看下图,不同客户端订阅了不同频道:


订阅的原理是:服务器将客户端与被订阅频道在pubsub_channels字典中进行关联。

假如客户端10086执行命令:

SUBSCRIBE "news.sport" "news.movie"

服务器完成两件事:

  • 将10086添加到sport链表后面
  • 新增一个键”news.movie”


退订就是在链表中删除客户端信息,如果退订后某个键没有任何客户端,则程序将从pubsub_channels字典中删除频道对应的键。

1.2 模式的订阅与退订

类似地,服务器也将所有模式的订阅关系都保存在服务器状态的pubsub_patterns属性。

与频道不同的是,这是一个链表,链表中的每个节点都包含着一个pubsubPattern结构,这个结构的pattern属性记录了被订阅的模式,而client属性则记录了订阅模式的客户端:

typedef struct pubsub_Pattern 
{ 
    // 订阅模式的客户端 
    redisClient *client;
    // 被订阅的模式 
    robj *pattern;
} pubsubPattern;

下面举一个例子:

  • 客户端7正在订阅模式”music.*”
  • 客户端8正在订阅模式”book.*”
  • 客户端9正在订阅模式”news.*”


订阅模式时,服务器会对每个被订阅的模式执行以下两个操作:

  1. 新建一个pubsubPattern结构,将结构的pattern属性设置为被订阅的模式,client属性设置为订阅模式的客户端。
  2. pubsubPattern结构添加到pubsub_patterns链表的表尾。

退订时,在pubsub_patterns链表中查找并删除。

2. 发布消息

发布消息有两个动作:

  1. 将消息发送给所有channel的订阅者。
  2. 如果有一个或多个模式与频道channel匹配,则同时将消息发送给模式的订阅者。

对于频道订阅者,首先服务器要在pubsub_channels中找到相对应的channel(一个链表),然后顺着这个链表,将消息发送给所有客户端。

对于模式订阅者,服务器会在pubsub_patterns链表中找到与channel频道相匹配的模式,然后将消息发送给订阅了这些模式的所有客户端。

3. 查看订阅消息

PUBSUB命令是Redis 2.8新增加的命令之一,客户端可以通过这个命令来查看频道或者模式的相关信息,比如某个频道目前有多少订阅者,又或者某个模式目前有多少订阅者。本节介绍这个命令的实现方法。

3.1 PUBSUB CHANNELS

PUBSUB CHANNELS[pattern]子命令用于返回服务器当前被订阅的频道,其中pattern参数是可选的:

  • 不给定,则返回所有被订阅的所有频道。
  • 给定,与pattern模式相匹配的频道。

比如:

redis> PUBSUB CHANNELS
1) "news.it"
2) "news.sport"
3) "news.business"
4) "news.movie"

redis> PUBSUB CHANNELS "news.[is]*"
1) "news.it"
2) "news.sport"

3.2 PUBSUB NUMSUB

PUBSUB NUMSUB[channel-1 channel-2...channel-n]子命令接受任意多个频道作为输入参数,并返回这些频道的订阅者数量

这个子命令是通过在pubsub_channels字典中找到频道对应的订阅者链表,然后返回订阅者链表的长度

3.3 PUBSUB NUMPAT

PUBSUB NUMPAT子命令用于返回服务器当前被订阅模式的数量

通过返回pubsub_patterns链表的长度来实现的。注意一下频道数量的查找逻辑是:频道字典->频道订阅者链表->数量;而模式数量的查找逻辑是:模式链表->数量