Redis 订阅发布模式底层实现
频道的订阅与退订
Redis 中有个 pubsub_channels 字典 ,里面保存了所有频道和订阅的关系。
struct redisServer{
dict *pubsub_channels;
};
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eSWKAEBc-1634819967212)(/Users/chenzhijie/Library/Application Support/typora-user-images/image-20211021195647122.png)]
-
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
字典中为频道创建一个键, 并将这个键的值设置为空链表, 然后再将客户端添加到链表, 成为链表的第一个元素。
频道订阅伪代码实现:
def subscribe(*all_input_channels):
# 遍历输入的所有频道
for channel in all_input_channels:
# 如果 channel 不存在于 pubsub_channels 字典(没有任何订阅者)
# 那么在字典中添加 channel 键,并设置它的值为空链表
if channel not in server.pubsub_channels:
server.pubsub_channels[channel] = []
# 将订阅者添加到频道所对应的链表的末尾
server.pubsub_channels[channel].append(client)
频道退订
unsubscribe 命令会进行品频道退订
- 程序会根据被退订频道的名字, 在
pubsub_channels
字典中找到频道对应的订阅者链表, 然后从订阅者链表中删除退订客户端的信息。 - 如果删除退订客户端之后, 频道的订阅者链表变成了空链表, 那么说明这个频道已经没有任何订阅者了, 程序将从
pubsub_channels
字典中删除频道对应的键。
这里和之前整理的文章中容器的规则相同:
- create if not exist:如果操作时不存在对应的容器,那么会先创建后再去操作。
- delete if no elements:如果容器中没有元素就会删除容器。
频道退订伪代码实现:
def unsubscribe(*all_input_channels):
# 遍历要退订的所有频道
for channel in all_input_channels:
# 在订阅者链表中删除退订的客户端
server.pubsub_channels[channel].remove(client)
# 如果频道已经没有任何订阅者了(订阅者链表为空)
# 那么将频道从字典中删除
if len(server.pubsub_channels[channel]) == 0:
server.pubsub_channels.remove(channel)
模式的订阅和退订
如果使用 psubscribe 来进行订阅,那么会订阅所有满足的频道,也被称作模式订阅。
在 Redis 的数据字段中有 pubsub_patterns 保存了所有模式的订阅情况。
struct redisServer {
// ...
list *pubsub_patterns;
// ...
};
typedef struct pubsubPattern {
redisClient *client;
robj *pattern;
} pubsubPattern;
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9iLodMx5-1634819967216)(/Users/chenzhijie/Library/Application Support/typora-user-images/image-20211021201352037.png)]
如果有新的客户端订阅了模式那么会继续添加到链表尾部,遍历 pubsub_patterns 链表,就可以得到所有被订阅的模式和订阅这些模式的客户端,从而向他们发送消息。
模式订阅
psubscribe 命令可以进行模式的订阅
模式订阅伪代码实现:
def psubscribe(client, *all_input_patterns){
# 遍历输入的所有模式
for pattern in all_input_patterns:
# 将 pubsubPattern 添加到模式订阅链表的末尾
server.pubsub_patterns.append(pubsubPattern(client, pattern))
}
模式退订
punsubscribe 命令可以进行模式退订
模式退订伪代码实现:
def punsubscribe(client){
# 遍历所有模式
for pattern in server.pubsub_patterns:
# 删除 client 订阅的模式
if match(pattern[client],client):
del(pattern)
}
发送消息
发送消息后会通知 订阅该频道的客户端 和 订阅模式包含该频道的客户端
发送消息伪代码实现:
def PUBLISH(channel, message):
# 遍历所有订阅频道 channel 的客户端
for client in server.pubsub_channels[channel]:
# 将信息发送给它们
send_message(client, message)
# 取出所有模式,以及订阅模式的客户端
for pattern, client in server.pubsub_patterns:
# 如果 channel 和模式匹配
if match(channel, pattern):
# 那么也将信息发给订阅这个模式的客户端
send_message(client, message)
上一篇: python 调用Windows API
下一篇: 操作系统实验之作业调度算法