发布与订阅
2023-4-9
| 2023-8-2
0  |  阅读时长 0 分钟
type
status
date
slug
summary
tags
category
icon
password
Property

 
频道订阅(SUBSCRIBE、PUBLISH)
通过执行SUBSCRIBE命令,客户端可以订阅一个或多个频道,从而成为这些频道的订阅者(subscriber):每当有其他客户端使用PUBLISH命令向被订阅的频道发送消息(message)时,频道的所有订阅者都会收到这条消息。
假设A、B、C三个客户端都执行了命令SUBSCRIBE "news.it",那么这三个客户端就是"news.it"频道的订阅者:
notion image
如果这时某个客户端执行命令PUBLISH "news.it" "hello",向"news.it"频道发送消息"hello",那么"news.it"三个订阅者都将收到这条消息
notion image
模式订阅(PSUBSCRIBE)
客户端还可以通过执行PSUBSCRIBE命令订阅一个或多个模式,从而成为这些模式的订阅者:每当有其他客户端向某个频道发送消息时,消息不仅会被发送给 这个频道的所有订阅者,它还会被发送给所有与这个频道相匹配的模式的订阅者。
假设,客户端A正在订阅频道"news.it",客户端B正在订阅频道"news.et",客户端C和客户端D正在订阅与"news.it"频道和"news.et"频道相匹配的模式"news.[ie]t"
notion image
 
如果这时某个客户端执行PUBLISH命令PUBLISH "news.it" "hello",向"news.it"频道发送消息"hello",那么不仅正在订阅"news.it"频道的客户端A会收到消 息,客户端C和客户端D也同样会收到消息,因为这两个客户端正在订阅匹配"news.it"频道的"news.[ie]t"模式:
notion image
与此类似,如果某个客户端执行命令PUBLISH "news.et" "world",向"news.et"频道发送消息"world",那么不仅正在订阅"news.et"频道的客户端B会收到消 息,客户端C和客户端D也同样会收到消息,因为这两个客户端正在订阅匹配"news.et"频道 的"news.[ie]t"模式:
notion image
 
 

频道的订阅与退订

当一个客户端执行SUBSCRIBE命令, 订阅某个或某些频道的时候, 这个客户端与被订阅频道之间就建立起了一种订阅关系。
Redis将所有频道的订阅关系都保存在服务器状态的pubsub_channels字典里面, 这个字典的键是某个被订阅的频道, 而键的值则是一个链表, 链表里面记录了所有订阅这个频道的客户端:
 
一个 pubsub_channels 字典示例, 这个字典记录了以下信息:
notion image
  • client-1 、 client-2 、 client-3 三个客户端正在订阅 "news.it" 频道。
  • 客户端client-4正在订阅"news.sport" 频道。
  • client-5client-6两个客户端正在订阅"news.business"频道
 

订阅频道

每当客户端执行SUBSCRIBE命令, 订阅某个或某些频道的时候, 服务器都会将客户端与被订阅的频道在pubsub_channels字典中进行关联。根据频道是否已经有其他订阅者, 关联操作分为两种情况执行:
  • 如果频道已经有其他订阅者, 那么它在pubsub_channels字典中必然有相应的订阅者链表, 程序唯一要做的就是将客户端添加到订阅者链表的末尾
  • 如果频道还未有任何订阅者, 那么它必然不存在于pubsub_channels字典, 程序首先要在pubsub_channels字典中为频道创建一个键, 并将这个键的值设置为空链表, 然后再将客户端添加到链表, 成为链表的第一个元素。
 
之后, pubsub_channels字典将更新, 其中用虚线包围的是新添加的节点:
notion image
  • 更新后的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”
notion image
notion image
图中用虚线包围的两个节点将被删除,因为删除client-10086之后, 频道"news.movie"已经没有任何订阅者, 因此键"news.movie"也从字典中被删除了
 
UNSUBSCRIBE命令的实现可以用以下伪代码来描述:
 
 

模式的订阅与退订

服务器将所有频道的订阅关系都保存在服务器状态的pubsub_channels属性里 面,与此类似,服务器也将所有模式的订阅关系都保存在服务器状态的pubsub_patterns属性里面:
pubsub_patterns属性是一个链表,链表中的每个节点都包含着一个pubsubPattern结构, 这个结构的pattern属性记录了被订阅的模式,而client属性则记录了订阅模式的客户端:
notion image
notion image
一个pubsub_patterns链表示例,这个链表记录了以下信息:
  • 客户端client-7正在订阅模式"music.*"
  • 客户端client-8正在订阅模式"book.*"
  • 客户端client-9正在订阅模式"news.*"
 

订阅模式

客户端还可以通过执行PSUBSCRIBE命令订阅一个或多个模式,从而成为这些模式的订阅者:每当有其他客户端向某个频道发送消息时,消息不仅会被发送给 这个频道的所有订阅者,它还会被发送给所有与这个频道相匹配的模式的订阅者。
每当客户端执行PSUBSCRIBE命令订阅某个或某些模式的时候,服务器会对每个被订阅的模式执行以下两个操作:
  • 新建一个pubsubPattern结构,将结构的pattern属性设置为被订阅的模式,client属性设置为订阅模式的客户端
  • pubsubPattern结构添加到pubsub_patterns链表的表尾
 
假设服务器中pubsub_patterns链表的当前状态如下图所示:
notion image
那么当客户端client-9执行命令PSUBSCRIBE "news.*”之后,pubsub_patterns链表将更至新如下图所示的状态,其中用虚线包围的是新添加的pubsubPattern结构:
notion image
PSUBSCRIBE命令的实现原理可以用以下伪代码来描述:
 

模式的退订

模式的退订命令PUNSUBSCRIBEPSUBSCRIBE命令的反操作:当一个客户端退订某个或某些模式的时候,服务器将在pubsub_patterns链表中查找并删除那些pattern属性为被退订模式,并且client属性为执行退订命令的客户端的pubsubPattern结构
假设服务器pubsub_patterns链表的当前状态如下图所示:
notion image
那么当客户端client-9执行命令PUNSUBSCRIBE "news.*”client属性为client-9pattern属性为"news.*"pubsubPattern结构将被删除, pubsub_patterns链表将更新至下图所示的样子:
notion image
PUNSUBSCRIBE命令的实现原理可以用以下伪代码来描述
 

发送消息

当一个客户端执行PUBLISH命令的时候,会将消息message发送给频道channel
PUBLISH命令执行完之后,服务器需要执行以下两个动作:
  • 将消息message发送给channel频道的所有订阅者
  • 如果有一个或多个模式pattern与频道channel相匹配,那么将消息message发送给pattern模式的订阅者
 

将消息发送给频道订阅者

因为服务器状态中的pubsub_channels字典记录了所有频道的订阅关系,所以为了将消息发送给channel频道的所有订阅者,PUBLISH命令要做的就是在pubsub_channels字典里找到频道channel的订阅者名单(一个链表),然后将消息发送给名单上的所有客户端
 
假设服务器pubsub_channels字典当前的状态如下图所示
notion image
如果这时某个客户端执行命令PUBLISH "news.it" "hello",那么PUBLISH命令将在pubsub_channels字典中查找键"news.it"对应的链表值,并通过遍历链表将消息"hello"发送给"news.it"频道的三个订阅者client-1client-2client-3
PUBLISH命令将消息发送给频道订阅者的方法可以用以下伪代码来描述:
 

将消息发送给模式订阅者

服务器状态中的pubsub_patterns链表记录了所有模式的订阅关系,所以为了将消息发送给所有与channel频道相匹配的模式的订阅者,PUBLISH命令要做的就是遍历整个pubsub_patterns链表,查找那些与channel频道相匹配的模式,并将消息发送给订阅了这些模式的客户端。
假设pubsub_patterns链表的当前状态如下图所示
notion image
如果这时某个客户端执行命令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字典的所有键(每个键都是一个被订阅的频道),然后记录并返回所有符合条件的频道来实现的。这个过程可以用以下伪代码来描述:
notion image
对于上图所示的pubsub_channels字典来说,执行PUBSUB CHANNELS命令将返回服务器目前被订阅的四个频道。执行PUBSUB CHANNELS"news.[is]*"命令将返回"news.it""news.sport"两个 频道,因为只有这两个频道和"news.[is]"模式相匹配。
 

PUBSUB NUMSUB命令

接受任意多个频道作为输入 参数,并返回这些频道的订阅者数量,命令格式如下:
这个子命令是通过pubsub_channels字典中找到频道对应的订阅者链表,然后返回订阅者链表的长度来实现的(订阅者链表的长度就是频道订阅者的数量)。这个过程可以用以下 伪代码来描述:
notion image
对于上图所示的pubsub_channels字典来说,对字典中的四个频道执行PUBSUB NUMSUB命令将获得以下回复:
notion image
 

PUBSUB NUMPAT命令

用于返回服务器当前被订阅模式的数量,命令格式如下:
这个子命令是通过返回pubsub_patterns链表的长度来实现的,因为这个链表的长度就是服务器被订阅模式的数量,这个过程可以用以下伪代码来描述:
对于下图所示的pubsub_patterns链表来说,执行PUBSUB NUMPAT命令将返回3:
notion image
 
  • Redis
  • 集群事务
    目录