目录
发布与订阅简介
Redis的发布与订阅功能由PUBLISH、SUBSCRIBE、PSUBSCRIBE等命令组成。
通过执行SUBSCRIBE命令,客户端可以订阅一个或多个频道,从而成为这些频道的订阅者(subscriber) : 每当有其他客户端向被订阅的频道发送消息(message)时,频道的所有订阅者都会收到这条消息。
在消费者下线的情况下,生产的消息会丢失,得使用专业的消息队列如RocketMQ等。
举个例子,假设A、B、C三个客户端都执行了命令:
那么这三个客户端就是"news.it"频道的 订阅者,如图所示。
如果这时某个客户端执行命令
向"news.it"频道发送消息"hello", 那么"news.it"的三个订阅者都将收到这 条消息,如图所示。
除了订阅频道之外,客户端还可以通过执行SUBSCRIBE命令订阅一个或多个模式从而成为这些模式的订阅者:每当有其 他客户端向某个频道发送消息时,消息不仅 会被发送给这个频道的所有订阅者,它还会被发送给所有与这个频道相匹配的模式的订阅者。
举个例子,假设如图18-3所示:
客户端A正在订阅频道"news.it"
客户端B正在订阅频道"news.et"
客户端C和客户端D正在订阅与"news.it"频道和"news.et"频道相匹配的模 式"news. [ie]t"
如果这时某个客户端执行命令向"news.it"频道发送消息"hello", 那么不仅正在订阅"news.it"频道的客户端 A会收到消息,客户端C和客户端D也同样会收到消息,因为这两个客户端正在订阅匹配 "news.it"频道的"news. [ie]t"模式,如图所示。
向"news.et"频道发送消息"world", 那么不仅正在订阅"news.et"频道的客户端 B会收到消息,客户端C和客户端D也同样会收到消息,因为这两个客户端正在订阅匹配 "news.et"频道的"news. [ie] t"模式,如图所示。
频道的订阅与退订
当一个客户端执行SUBSCRIBE命令订阅某个或某些频道的时候,这个客户端与被订阅 频道之间就建立起了一种订阅关系。
Redis将所有频道的订阅关系都保存在服务器状态的pubsub_channels字典里面, 这个字典的键是某个被订阅的频道,而键的值则是一个链表,链表里面记录了所有订阅这个 频道的客户端:
比如说,图就展示了一个pubsub_channels字典示例,这个字典记录了以下信息:
client-1、client-2、client-3三个客户端正在订阅,'news.it"频道。
客户端client-4正在订阅"news.sport"频道。
client-5和client-6两个客户端正在订阅"news.business"频道。
订阅频道
每当客户端执行SUBSCRIBE命令订阅某个或某些频道的时候,服务器都会将客户端与 被订阅的频道在pubsub_channels字典中进行关联。
根据频道是否已经有其他订阅者,关联操作分为两种情况执行:
如果频道已经有其他订阅者,那么它在pubsub_channels字典中必然有相应的订 阅者链表,程序唯一要做的就是将客户端添加到订阅者链表的末尾。
如果频道还未有任何订阅者,那么它必然不存在于pubsub_channels字典,程序 首先要在pubsub_channels字典中为频道创建一个键,并将这个键的值设置为空 链表,然后再将客户端添加到链表,成为链表的第一个元素。
举个例子,假设服务器pubsub_channels字典的当前状态如图所示
那么当客 户端client-10086执行命令
SUBSCRIBE "news.sport" "news.movie"
之后,pubsub_channels字典将更新至图所示的状态,其中用虚线包围的是新添加的节点:
更新后的pubsub_channels字典新增了"news.movie"键,该键对应的链表值 只包含一个client-10086节点,表示目前只有client-10086一个客户端在订 阅"news.movie"频道。
至于原本就已经有客户端在订阅的"news.sport"频道,client-10086的节点 放在了频道对应链表的末尾,排在client-4节点的后面。
SUBSCRIBE命令的实现可以用以下伪代码来描述:
退订频道
UNSUBSCRJBE命令的行为和SUBSCRIBE命令的行为正好相反,当一个客户端退订某 个或某些频道的时候,服务器将从pubsub_channels中解除客户端与被退订频道之间的 关联:
程序会根据被退订频道的名字,在pubsub_channels字典中找到频道对应的订阅 者链表,然后从订阅者链表中删除退订客户端的信息。
如果删除退订客户端之后,频道的订阅者链表变成了空链表,那么说明这个频道已 经没有任何订阅者了,程序将从pubsub_channels字典中删除频道对应的键。
举个例子,假设pubsub_channels的当前状态如图所示
那么当客户端 client一10086执行命令
UNSUBSCRIBE "news.sport" "news.movie"
之后,图中用虚线包围的两个节点将被删除(如图所示):
在pubsub_channels字典更新之后,client-10086的信息已经从"news. sport"频道和"news .mo"Vie"频道的订阅者链表中被删除了。
另外,因为删除client-10086之后,频道"news. movie"已经没有任何订阅 者,因此键"news.movie"也从字典中被删除了。
UNSUBSCRIBE命令的实现可以用以下伪代码来描述:
模式的订阅与退订
前面说过,服务器将所有频道的订阅关系都保存在服务器状态的pubsub_channels 属性里面,与此类似,服务器也将所有模式的订阅关系都保存在服务器状态的pubsub_ patterns属性里面: