读书笔记:《Redis设计与实现》之发布订阅

news/2024/11/16 13:19:46/

发布与订阅简介

命令

  • SUBSCRIBE: 订阅一个频道
SUBSCRIBE channel [channel ...]
  • SUBSCRIBE: 向一个频道发送信息
PUBLISH channel message
  • UNSUBSCRIBE: 取消订阅一个频道
UNSUBSCRIBE [channel [channel ...]]
  • PSUBSCRIBE:订阅一个或多给定模式的频道
PSUBSCRIBE pattern [pattern ...]
  • PUNSUBSCRIBE: 取消订阅一个或多个给定模式的频道
PUNSUBSCRIBE [pattern [pattern ...]]

示例

127.0.0.1:6379> SUBSCRIBE chan1
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "chan1"
3) (integer) 1
127.0.0.1:6379> PUBLISH chan1 "Hello World!"
(integer) 1
1) "message"
2) "chan1"
3) "Hello World!"
127.0.0.1:6379> UNSUBSCRIBE chan1
Reading messages... (press Ctrl-C to quit)
1) "unsubscribe"
2) "chan1"
3) (integer) 0
127.0.0.1:6379> PSUBSCRIBE chan*
Reading messages... (press Ctrl-C to quit)
1) "psubscribe"
2) "chan*"
3) (integer) 1
1) "pmessage"
2) "chan*"
3) "chan1"
4) "Hello World!"

实现原理

频道的订阅与退订

  • 当一个客户端执行了SUBSCRIBE命令订阅某个或者某些频道时,这个客户端与被订阅频道之间建立起一个订阅关系.
    redis 将所有频道的订阅关系保存在pubsub_channels字典中,这个字典的键是某个被订阅的频道,值是一个链表,记录了所有订阅这个频道的客户端。
struct redisServer {// ...//保存所有频道的订阅关系dict *pubsub_channels;// ...
};

订阅频道

  • 每当客户端执行SUBSCRIBE命令订阅某个或某些频道的时候,服务器都会将客户端与被订阅的频道在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命令的行为和SUBSCRIBE命令的行为正好相反,当一个客户端退订某个或某些频道的时候,服务器将从pubsub_channels中解除客户端与被退订频道之间的关联
  • 伪代码
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)

模式的订阅与退订

  • 与频道订阅类似,服务器也将所有模式的订阅关系都保存在服务器状态的pubsub_patterns属性里面

模式订阅

每当客户端执行PSUBSCRIBE命令订阅某个或某些模式的时候,服务器会对每个被订阅的模式执行以下两个操作:

  1. 将客户端与被订阅的模式在pubsub_patterns字典中进行关联
  2. 遍历所有已经存在的键,检查这些键是否与被订阅的模式匹配。如果某个键与模式匹配,那么服务器就会将这个客户端添加到与这个键相关的订阅者链表的末尾
  • 伪代码
def psubscribe(*all_input_patterns):#遍历输入的所有模式for pattern in all_input_patterns:#创建新的pubsubPattern结构#记录被订阅的模式,以及订阅模式的客户端pubsubPattern = create_new_pubsubPattern()pubsubPattern.client = clientpubsubPattern.pattern = pattern#将新的pubsubPattern追加到pubsub_patterns链表末尾server.pubsub_patterns.append(pubsubPattern)

模式退订

  • 伪代码
def punsubscribe(*all_input_patterns):# 遍历所有要退订的模式for pattern in all_input_patterns:# 遍历pubsub_patterns链表中的所有pubsubPattern结构for pubsubPattern in server.pubsub_patterns:# 如果当前客户端和pubsubPattern记录的客户端相同# 并且要退订的模式也和pubsubPattern记录的模式相同if client == pubsubPattern.client and \pattern == pubsubPattern.pattern:# 那么将这个pubsubPattern从链表中删除server.pubsub_patterns.remove(pubsubPattern)

发送消息

当一个Redis客户端执行PUBLISH<channel><message>命令将消息message发送给频道channel的时候,服务器需要执行以下两个动作:

  • 1)将消息message发送给channel频道的所有订阅者。
  • 2)如果有一个或多个模式pattern与频道channel相匹配,那么将消息message发送给pattern模式的订阅者
  • 伪代码
def channel_publish(channel, message):#如果channel键不存在于pubsub_channels字典中#那么说明channel频道没有任何订阅者#程序不做发送动作,直接返回if channel not in server.pubsub_channels:return#运行到这里,说明channel频道至少有一个订阅者#程序遍历channel频道的订阅者链表#将消息发送给所有订阅者for subscriber in server.pubsub_channels[channel]:send_message(subscriber, message)

查看订阅信息

  • PUBSUB CHANNELS[pattern]子命令用于返回服务器当前被订阅的频道
  • PUBSUB NUMSUB[channel-1 channel-2…channel-n]子命令接受任意多个频道作为输入参数,并返回这些频道的订阅者数量。
  • PUBSUB NUMPAT子命令用于返回服务器当前被订阅模式的数量。

重点回顾

  • 服务器状态在pubsub_channels字典保存了所有频道的订阅关系:SUBSCRIBE命令负责将客户端和被订阅的频道关联到这个字典里面,而UNSUBSCRIBE命令则负责解除客户端和被退订频道之间的关联。
  • 服务器状态在pubsub_patterns链表保存了所有模式的订阅关系:PSUBSCRIBE命令负责将客户端和被订阅的模式记录到这个链表中,而PUNSUBSCRIBE命令则负责移除客户端和被退订模式在链表中的记录。
  • PUBLISH命令通过访问pubsub_channels字典来向频道的所有订阅者发送消息,通过访问pubsub_patterns链表来向所有匹配频道的模式的订阅者发送消息。
  • PUBSUB命令的三个子命令都是通过读取pubsub_channels字典和pubsub_patterns链表中的信息来实现的。

http://www.ppmy.cn/news/1547458.html

相关文章

Linux 进程信号初识

目录 0.前言 1.什么是信号 1.1生活中的信号 1.2 OS中的信号 2.认识信号 2.1信号概念 2.2查看信号 2.3 signal函数 2.4代码示例 3. 信号处理方式 3.1 忽略信号 3.2 默认处理 3.3 自定义处理 4.小结 (图像由AI生成) 0.前言 在之前的学习中,我…

SHELL脚本编写基础(2)永久环境变量和字符串显位

声明! 学习视频来自B站up主 **泷羽sec** 有兴趣的师傅可以关注一下,如涉及侵权马上删除文章,笔记只是方便各位师傅的学习和探讨,文章所提到的网站以及内容,只做学习交流,其他均与本人以及泷羽sec团队无关&a…

M4 lotus 源码编译安装

查看系统版本 sw_vers ProductName: macOS ProductVersion: 15.1安装依赖 xcode-select -p /Library/Developer/CommandLineToolsbrew install go jq pkg-config hwloc coreutilscurl --proto https --tlsv1.2 -sSf https://sh.rustup.rs | sh设置环境变量 export LIBRARY…

直接映射缓存配置

对于一个直接映射(Direct-Mapped)缓存,其缓存总大小为16字节,而每条cache line的大小为4字节,可以理解为以下几个方面: 1. 缓存结构 缓存大小(Cache Size):整个缓存空间…

Ceph 中Crush 算法的理解

Crush(Controlled Replication Under Scalable Hashing)算法是一种可扩展的、分布式的副本数据放置算法,广泛用于存储系统中,特别是Ceph分布式存储系统中。以下是对CRUSH算法的详细解释: 一、算法原理 CRUSH算法根据…

【LeetCode】每日一题 2024_11_15 最少翻转次数使二进制矩阵回文 I(模拟、矩阵遍历(竖着遍历))

前言 每天和你一起刷 LeetCode 每日一题~ 决定在前言里面加上新内容!新增模块:“本期看点” 本期看点:如何竖着遍历矩阵? LeetCode 启动! 题目:最少翻转次数使二进制矩阵回文 I 代码与解题思路 先读题…

生成自签名证书并配置 HTTPS 使用自签名证书

生成自签名证书 1. 运行 OpenSSL 命令生成证书和私钥 在终端中输入以下命令,生成自签名证书和私钥文件: sudo openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout self_signed.key -out self_signed.pem-x509:生成自签名证书。…

DB-GPT系列(五):DB-GPT六大基础应用场景part2

前面文章《DB-GPT系列(四):DB-GPT六大基础应用场景part1》讲了DB-GPT六大基础应用场景中的基础问答、知识库问答、Chat Excel功能,这篇文章继续介绍剩下的3个基础应用场景:Chat DB、Chat Data、Chat Dashboard。 一、…