type
status
date
slug
summary
tags
category
icon
password
Property
频道订阅(SUBSCRIBE、PUBLISH)
通过执行
SUBSCRIBE
命令,客户端可以订阅一个或多个频道,从而成为这些频道的订阅者(subscriber):每当有其他客户端使用PUBLISH命令向被订阅的频道发送消息(message)时,频道的所有订阅者都会收到这条消息。假设A、B、C三个客户端都执行了命令
SUBSCRIBE "news.it"
,那么这三个客户端就是"news.it"
频道的订阅者:如果这时某个客户端执行命令
PUBLISH "news.it" "hello"
,向"news.it"
频道发送消息"hello"
,那么"news.it"
的三个订阅者都将收到这条消息:模式订阅(PSUBSCRIBE)
客户端还可以通过执行
PSUBSCRIBE
命令订阅一个或多个模式,从而成为这些模式的订阅者:每当有其他客户端向某个频道发送消息时,消息不仅会被发送给 这个频道的所有订阅者,它还会被发送给所有与这个频道相匹配的模式的订阅者。假设,客户端A正在订阅频道
"news.it"
,客户端B正在订阅频道"news.et"
,客户端C和客户端D正在订阅与"news.it"
频道和"news.et"
频道相匹配的模式"news.[ie]t"
如果这时某个客户端执行
PUBLISH
命令PUBLISH "news.it" "hello"
,向"news.it"
频道发送消息"hello"
,那么不仅正在订阅"news.it"
频道的客户端A会收到消 息,客户端C和客户端D也同样会收到消息,因为这两个客户端正在订阅匹配"news.it"
频道的"news.[ie]t"
模式:与此类似,如果某个客户端执行命令
PUBLISH "news.et" "world"
,向"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
字典将更新, 其中用虚线包围的是新添加的节点:- 更新后的
pubsub_channels
字典新增了"news.movie"
键, 该键对应的链表值只包含一个client-10086
节点, 表示目前只有client-10086
一个客户端在订阅"news.movie"
频道。
- 至于原本就已经有客户端在订阅的
"news.sport"
频道,client-10086
的节点放在了频道对应链表的末尾, 排在client-4
节点的后面。
SUBSCRIBE
命令的实现可以用以下伪代码来描述:退订频道
UNSUBSCRIBE
命令的行为和SUBSCRIBE
命令的行为正好相反 —— 当一个客户端退订某个或某些频道的时候, 服务器将从pubsub_channels
中解除客户端与被退订频道之间的关联:- 程序会根据被退订频道的名字, 在
pubsub_channels
字典中找到频道对应的订阅者链表, 然后从订阅者链表中删除退订客户端的信息
- 如果删除退订客户端之后, 频道的订阅者链表变成了空链表, 那么说明这个频道已经没有任何订阅者了, 程序将从
pubsub_channels
字典中删除频道对应的键。
假设
pubsub_channels
的当前状态如所示, 那么当客户端 client-10086
执行命令:UNSUBSCRIBE "news.sport" "news.movie”
图中用虚线包围的两个节点将被删除,因为删除
client-10086
之后, 频道"news.movie"
已经没有任何订阅者, 因此键"news.movie"
也从字典中被删除了UNSUBSCRIBE
命令的实现可以用以下伪代码来描述:模式的订阅与退订
服务器将所有频道的订阅关系都保存在服务器状态的
pubsub_channels
属性里 面,与此类似,服务器也将所有模式的订阅关系都保存在服务器状态的pubsub_patterns
属性里面:pubsub_patterns
属性是一个链表,链表中的每个节点都包含着一个pubsubPattern
结构, 这个结构的pattern
属性记录了被订阅的模式,而client
属性则记录了订阅模式的客户端:一个
pubsub_patterns
链表示例,这个链表记录了以下信息:- 客户端
client-7
正在订阅模式"music.*"
- 客户端
client-8
正在订阅模式"book.*"
- 客户端
client-9
正在订阅模式"news.*"
订阅模式
客户端还可以通过执行
PSUBSCRIBE
命令订阅一个或多个模式,从而成为这些模式的订阅者:每当有其他客户端向某个频道发送消息时,消息不仅会被发送给 这个频道的所有订阅者,它还会被发送给所有与这个频道相匹配的模式的订阅者。每当客户端执行PSUBSCRIBE命令订阅某个或某些模式的时候,服务器会对每个被订阅的模式执行以下两个操作:
- 新建一个
pubsubPattern
结构,将结构的pattern
属性设置为被订阅的模式,client
属性设置为订阅模式的客户端
- 将
pubsubPattern
结构添加到pubsub_patterns
链表的表尾
假设服务器中
pubsub_patterns
链表的当前状态如下图所示:那么当客户端
client-9
执行命令PSUBSCRIBE "news.*”
之后,pubsub_patterns
链表将更至新如下图所示的状态,其中用虚线包围的是新添加的pubsubPattern
结构:PSUBSCRIBE
命令的实现原理可以用以下伪代码来描述:模式的退订
模式的退订命令
PUNSUBSCRIBE
是PSUBSCRIBE
命令的反操作:当一个客户端退订某个或某些模式的时候,服务器将在pubsub_patterns
链表中查找并删除那些pattern
属性为被退订模式,并且client
属性为执行退订命令的客户端的pubsubPattern
结构假设服务器
pubsub_patterns
链表的当前状态如下图所示:那么当客户端client-9执行命令
PUNSUBSCRIBE "news.*”
,client
属性为client-9
,pattern
属性为"news.*"
的pubsubPattern
结构将被删除, pubsub_patterns
链表将更新至下图所示的样子:PUNSUBSCRIBE
命令的实现原理可以用以下伪代码来描述发送消息
当一个客户端执行
PUBLISH
命令的时候,会将消息message
发送给频道channel
PUBLISH
命令执行完之后,服务器需要执行以下两个动作:- 将消息
message
发送给channel
频道的所有订阅者
- 如果有一个或多个模式
pattern
与频道channel
相匹配,那么将消息message
发送给pattern
模式的订阅者
将消息发送给频道订阅者
因为服务器状态中的
pubsub_channels
字典记录了所有频道的订阅关系,所以为了将消息发送给channel
频道的所有订阅者,PUBLISH
命令要做的就是在pubsub_channels
字典里找到频道channel
的订阅者名单(一个链表),然后将消息发送给名单上的所有客户端假设服务器
pubsub_channels
字典当前的状态如下图所示如果这时某个客户端执行命令
PUBLISH "news.it" "hello"
,那么PUBLISH
命令将在pubsub_channels
字典中查找键"news.it"
对应的链表值,并通过遍历链表将消息"hello"
发送给"news.it"
频道的三个订阅者:client-1
、client-2
和client-3
:PUBLISH
命令将消息发送给频道订阅者的方法可以用以下伪代码来描述:将消息发送给模式订阅者
服务器状态中的
pubsub_patterns
链表记录了所有模式的订阅关系,所以为了将消息发送给所有与channel
频道相匹配的模式的订阅者,PUBLISH
命令要做的就是遍历整个pubsub_patterns
链表,查找那些与channel
频道相匹配的模式,并将消息发送给订阅了这些模式的客户端。
假设
pubsub_patterns
链表的当前状态如下图所示如果这时某个客户端执行命令
PUBLISH "news.it" "hello"
,那么PUBLISH
命令会首先将消息"hello"
发送给"news.it"
频道的所有订阅者,然后开始在 pubsub_patterns
链表中查找是否有被订阅的模式与"news.it"
频道相匹配,结果发现"news.it"
频道和客户端client-9
订阅的"news.*"
频道匹配,于是命令将消息"hello"
发送给客户端client-9
。
PUBLISH
命令将消息发送给模式订阅者的方法可以用以下伪代码来描述:查看订阅信息
PUBSUB CHANNELS
用于返回服务器当前被订阅的频道,命令格式如下:
其中
pattern
参数是可选的:- 如果不给定
pattern
参数,那么命令返回服务器当前被订阅的所有频道
- 如果给定
pattern
参数,那么命令返回服务器当前被订阅的频道中那些与pattern
模式相匹配的频道
这个子命令是通过遍历服务器
pubsub_channels
字典的所有键(每个键都是一个被订阅的频道),然后记录并返回所有符合条件的频道来实现的。这个过程可以用以下伪代码来描述:对于上图所示的
pubsub_channels
字典来说,执行PUBSUB CHANNELS
命令将返回服务器目前被订阅的四个频道。执行PUBSUB CHANNELS"news.[is]*
"
命令将返回"news.it"
和"news.sport"
两个 频道,因为只有这两个频道和"news.[is]"
模式相匹配。PUBSUB NUMSUB命令
接受任意多个频道作为输入 参数,并返回这些频道的订阅者数量,命令格式如下:
这个子命令是通过在
pubsub_channels
字典中找到频道对应的订阅者链表,然后返回订阅者链表的长度来实现的(订阅者链表的长度就是频道订阅者的数量)。这个过程可以用以下 伪代码来描述:对于上图所示的
pubsub_channels
字典来说,对字典中的四个频道执行PUBSUB NUMSUB
命令将获得以下回复:PUBSUB NUMPAT命令
用于返回服务器当前被订阅模式的数量,命令格式如下:
这个子命令是通过返回
pubsub_patterns
链表的长度来实现的,因为这个链表的长度就是服务器被订阅模式的数量,这个过程可以用以下伪代码来描述:对于下图所示的
pubsub_patterns
链表来说,执行PUBSUB NUMPAT
命令将返回3: