Redis的发布与订阅功能由PUBLISH、SUBSCRIBE、PSUBSCRIBE、PUBSUB等命令组成。
SUBSCRIBE命令:客户端可以订阅一个或多个频道,从而成为这些频道的订阅者(subscriber),每当有其他客户端向被订阅的频道发送消息(message)时,频道的所有订阅者都会收到这条消息。
PSUBSCRIBE命令:除了订阅频道之外,客户端还可以通过执行PSUBSCRIBE命令订阅一个或多个模式,从而成为这些模式的订阅者:每当有其他客户端向某个频道发送消息时,消息不仅会被发送给这个频道的所有订阅者,它还会被发送给所有与这个频道相匹配的模式的订阅者。
UNSUBSCRIBE命令:退订频道。
PUBLISH命令:客户端通过publish命令向频道发送信息,频道的订阅者以及该频道匹配的模式的订阅者都会收到这条信息。
PUNSUBSCRIBE命令:退订模式。
PUBSUB CHANNELS [pattern]:查询客户端的订阅频道。pattern参数是可选,如果不给定pattern参数,那么命令返回服务器当前被订阅的所有频道。如果给定pattern参数,那么命令返回服务器当前被订阅的频道中那些与pattern模式相匹配的频道。
PUBSUB NUMSUB[channel-1 channel-2...channel-n]命令:接受任意多个频道作为输入参数,并返回这些频道的订阅者数量。
PUBSUB NUMPAT命令:用于返回服务器当前被订阅模式的数量。
频道
订阅与退订
Redis将所有频道的订阅关系都保存在服务器状态的pubsub_channels字典里面,这个字典的键是某个被订阅的频道,而键的值则是一个链表,链表里面记录了所有订阅这个频道的客户端。
struct redisServer {
...
//保存所有频道的订阅关系
dict *pubsub_channels;
...
};
当客户端订阅一个频道,Redis会在pubsub_channels字典里找到对应的频道键(没有则新创建一个键),讲客户端添加到列表尾部。
当客户端退订一个频道,Redis会在pubsub_channels字典里找到对应的频道键,讲客户端从列表中删除。如果删除退订客户端后列表为空,说明这个频道没有订阅者了,那么Redis会删除对应的键。
发送消息
因为服务器状态中的pubsub_channels字典记录了所有频道的订阅关系,所以为了将消息发送给channel频道的所有订阅者,PUBLISH命令要做的就是在pubsub_channels字典里找到频道channel的订阅者名单(一个链表),然后将消息发送给名单上的所有客户端。
模式
订阅与退订
与频道类似,服务器也将所有模式的订阅关系都保存在服务器状态的pubsub_patterns属性里面。
struct redisServer {
...
//保存所有模式订阅关系
list *pubsub_patterns;
...
};
pubsub_patterns属性是一个链表,链表中的每个节点都包含着一个pubsub Pattern结构,这个结构的pattern属性记录了被订阅的模式,而client属性则记录了订阅模式的客户端。
typedef struct pubsubPattern {
//订阅模式的客户端
redisClient *client;
//被订阅的模式
robj *pattern;
} pubsubPattern;
下图展示了一个pubsub_patterns链表示例,展示了信息:客户端client-7正在订阅模式"music.*";客户端client-8正在订阅模式"book.*";客户端client-9正在订阅模式"news.*"。
当客户端订阅某个模式时,Redis会新建一个pubsubPattern结构,将结构的pattern属性设置为被订阅的模式,client属性设置为订阅模式的客户端。并把这个结构添加到pubsub_patterns链表的表尾。
当客户端退订某个模式时,Redis将会在pubsub_patterns链表中删除掉对应的pubsubPattern结构。
发送消息
因为服务器状态中的pubsub_patterns链表记录了所有模式的订阅关系,所以为了将消息发送给所有与channel频道相匹配的模式的订阅者,PUBLISH命令要做的就是遍历整个pubsub_patterns链表,查找那些与channel频道相匹配的模式,并将消息发送给订阅了这些模式的客户端。