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

发布与订阅简介

命令

  • 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.xdnf.cn/news/16795.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

计算机提示mfc140u.dll丢失的五种解决方法,了解mfc140u.dll错误的几种修复方法

当你尝试打开某些程序时,突然出现错误提示,告知你系统缺少 mfc140u.dll 文件,这可能让你感到困惑和无助。mfc140u.dll 是 Microsoft Foundation Class (MFC) 库的一部分,对于运行很多由 Visual Studio 2015 使用 MFC 开发的应用程…

【SSL-RL】自监督强化学习: 好奇心驱动探索 (CDE)算法

📢本篇文章是博主强化学习(RL)领域学习时,用于个人学习、研究或者欣赏使用,并基于博主对相关等领域的一些理解而记录的学习摘录和笔记,若有不当和侵权之处,指出后将会立即改正,还望谅…

Windows系统 ElasticSearch,Kibana安装

目录 1.wins安装ElasticSearch2.将 elasticsearch 以服务的方式安装3. 在系统环境变量 Path 中添加如下路径4.启动点击即可5.双击 elasticsearch.bat 启动 elasticsearch 服务6.启动后第一次会显示一些配置信息,包括默认的用户密码 先记住 记不住的话可以重置密码7.验证安装结果…

《Probing the 3D Awareness of Visual Foundation Models》论文解析——单图像表面重建

一、论文简介 论文讨论了大规模预训练产生的视觉基础模型在处理任意图像时的强大能力,这些模型不仅能够完成训练任务,其中间表示还对其他视觉任务(如检测和分割)有用。研究者们提出了一个问题:这些模型是否能够表示物体…

P3-4.【结构化程序设计】第四节——知识要点:break、continue和goto辅助循环设计语句

知识要点:break、continue和goto辅助循环设计语句 视频: P3-4.1.【结构化程序设计】第四节——知识要点:break、continue和goto辅助循环设计语句 P3-4.2.【结构化程序设计】第四节——知识要点:break、continue和goto辅助循环设计…

灵神DAY3 KMP算法

具体解释: 1. 真前缀和真后缀的定义 前缀:字符串的起始部分。例如,字符串 s "aabcaa" 的前缀是 ""、"a"、"aa"、"aab"、"aabc"、"aabca"、"aabcaa"。 …

MySQL5.7.37安装配置

1.下载MySQL软件包并解压 2.配置环境变量 3.新建my.ini文件并输入信息 [mysqld] #端口号 port 3306 #mysql-5.7.27-winx64的路径 basedirC:\mysql-5.7.37\mysql-5.7.37-winx64 #mysql-5.7.27-winx64的路径\data datadirC:\mysql-5.7.37\mysql-5.7.37-winx64\data #最大连接数…

基于单片机的手持金属探测仪设计

本设计以STM32F103C8T6单片机为核心,通过金属线圈感应器来判断是否存在金属,控制OLED显示屏显示金属探测仪的灵敏度和参考值,通过电源模块将220V转化为3.3V对单片机进行供电,还可以通过按键对金属探测仪的灵敏度进行设置&#xff…

P1197 星球大战(并查集+逆向思维)

这是今天写的比较有价值的一道题,晚上写了大概一个多小时,主要还是在debug,出得很妙,好题👍 P1197 [JSOI2008] 星球大战 - 洛谷 | 计算机科学教育新生态 思路:如果我们按照顺序一个一个的去计算毁灭一个星…

深度学习驱动的蛋白质设计技术与前沿实践-从基础到尖端应用

RoseTTAFold,作为David Baker教授团队早期开发的蛋白质结构预测工具,在学术界与工业界广受认可。然而,随着时间推移,仅局限于预测已知结构的蛋白质并不能满足生物医药和生物工程领域对创新设计的需求。这促使David Baker教授团队继…

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.前言 在之前的学习中,我…

SpringBoot(二十五)SpringBoot集成JRebel实现热更新

今天来安装一个IDEA代码热更新的插件,一个神器。 我们之前也为IDEA配置了热更新,使用的是spring-boot-devtools插件。具体请移步《SpringBoot(一)创建项目及配置IDEA热更新》 上边这个热更新对于单模块项目是没有问题的,但是对于多模块项目可能就无能无能为力了,而且,随…

MATLAB中的绘图技巧

MATLAB作为一种强大的科学计算软件,不仅可以进行数据分析和模拟,还具有出色的绘图功能。本文介绍若干在MATLAB中绘图的技巧和方法,帮助使用者更好地呈现数据和结果 文章目录 基本绘图函数高级绘图技巧三维绘图动态绘图绘图工具结语 基本绘图函…

java八股-AQS,Reentrantlock

什么是AQS? 难度:★★★☆☆ 考频:★★★☆☆ 注意这个队列是双向队列,每次有线程释放锁了之后,会有下一个线程来,以及队列头元素,如果设置的是公平锁,那么是等了很久的头元素先获…

python——模块 迭代器 正则

一、python模块 先创建一个 .py 文件,这个文件就称之为 一个模块 Module。 使用模块的优点: 模块化编程,多文件编程 1.2 模块的使用 1.2.1 import语句 想要B.py文件中,使用A.py文件,只需要在B.py文件中使用关键字…

STL之mapset|AVL树

STL之map&set|AVL树 set&map搜索二叉树实现代码 set的使用map的使用set&map的模拟实现(见红黑树篇) AVL树AVL树的模拟实现 set&map 前言:stl库中set和map的底层都是红黑树,一种平衡搜索二叉树,是我下…

使用阿里云快速搭建 DataLight 平台

使用阿里云快速搭建 DataLight 平台 本篇文章由用户 “闫哥大数据” 分享,B 站账号:https://space.bilibili.com/357944741?spm_id_from333.999.0.0 注意:因每个人操作顺序可能略有区别,整个部署流程如果出现出入,以…

OceanBase 分区表详解

1、分区表的定义 在OceanBase数据库中,普通的表数据可以根据预设的规则被分割并存储到不同的数据区块中,同一区块的数据是在一个物理存储上。这样被分区块的表被称为分区表,而其中的每一个独立的数据区块则被称为一个分区。 如下图所示&…

代码随想录算法训练营第三十八天 | 322.零钱兑换 279.完全平方数 139.单词拆分 多重背包以及背包总结

LeetCode 322.零钱兑换: 文章链接 题目链接:322.零钱兑换 思路: 首先分析题目,每种硬币的数量是无限的,因此为完全背包问题;又要求返回的是最少硬币个数,因此与组合数/排列数无关&#xff0c…

计算机网络WebSocket——针对实习面试

目录 计算机网络WebSocket什么是WebSocket?WebScoket和HTTP协议的区别是什么?说明WebSocket的优势和使用场景?说明WebSocket的建立连接的过程? 计算机网络WebSocket 什么是WebSocket? WebSocket是一个网络通信协议,提…