欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Redis 订阅发布模式底层实现

程序员文章站 2022-07-05 10:58:12
...

频道的订阅与退订

Redis 中有个 pubsub_channels 字典 ,里面保存了所有频道和订阅的关系。

struct redisServer{
	dict *pubsub_channels;
};

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eSWKAEBc-1634819967212)(/Users/chenzhijie/Library/Application Support/typora-user-images/image-20211021195647122.png)]

  • client-1client-2client-3 三个客户端正在订阅 "news.it" 频道。
  • 客户端 client-4 正在订阅 "news.sport" 频道。
  • client-5client-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 字典中删除频道对应的键。

这里和之前整理的文章中容器的规则相同:

  1. create if not exist:如果操作时不存在对应的容器,那么会先创建后再去操作。
  2. 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)