Redis 订阅发布模式底层实现

频道的订阅与退订

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)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值