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

ops/2024/11/17 21:50:51/

发布与订阅简介

命令

  • 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/ops/134238.html

相关文章

【机器学习】机器学习中用到的高等数学知识-5. 函数空间和泛函分析 (Functional Analysis)

函数的连续性和可微性:在评估模型的学习能力和泛化能力时非常重要。希尔伯特空间和巴拿赫空间:在支持向量机(SVM)和神经网络中用于理解高维数据。 1. 函数的连续性和可微性 函数的 连续性 和 可微性 是分析和优化模型的重要数学…

【EasyExcel等比例缩小导出图片】

EasyExcel等比例缩小导出图片 一、背景二、思路三、代码 一、背景 使用EasyExcel导出excel文件,但是需要同时导出图片信息,且图片信息不能影响行高和单元格宽度,图片本身被导出时,不能因为压缩导致图片变形 二、思路 使用EasyE…

关于在Reverse函数中不能使用L=s而是*L=*s的原因分析

完整代码地址: https://blog.csdn.net/2301_76819732/article/details/143807340?spm1001.2014.3001.5502 如果使用Ls; 的话,当输出结果时,会发现内容为空。 我感到很奇怪,按照我的设想,Ls;会把s指向的地址赋给L。 但…

1.两数之和-力扣(LeetCode)

题目: 解题思路: 在解决这个问题之前,首先要明确两个点: 1、参数returnSize的含义是返回答案的大小(数目),由于这里的需求是寻找数组中符合条件的两个数,那么当找到这两个数时&#…

「Qt Widget中文示例指南」如何创建一个窗口标志?(二)

Qt 是目前最先进、最完整的跨平台C开发工具。它不仅完全实现了一次编写,所有平台无差别运行,更提供了几乎所有开发过程中需要用到的工具。如今,Qt已被运用于超过70个行业、数千家企业,支持数百万设备及应用。 窗口标志要么是类型…

数仓建设之Oracle常见语法学习

1. 字符串截取 select substr(AAA-BBB, 1, instr(AAA-BBB, -, -1) - 1) 值 from dual; --AAA select substr(AAA-BBB, instr(AAA-BBB, -, -1) 1) 值 from dual; --BBB2. 帆软报表有参数SQL select a.agency_code, a.agency_name, a.agency_typefrom dw.dim_ta_subred_agency…

Ubuntu22.04.2 k8s部署

k8s介绍 简单介绍 通俗易懂的解释: Kubernetes(也被称为 K8s)就像是一个大管家,帮你管理你的云计算服务。想象一下,你有很多个小程序(我们称之为“容器”),每个都在做不同的事情&…

Tiktok对接和内容发布申请流程

这段时间在搞AI生成视频,希望用户能一键发布到Tiktok,因此研究了一下Tiktok的开发者申请流程,发现好复杂,同时也发现Tiktok的开发也跟我一样,挺草台班子的 0、流程简述 废话不多说,Tiktok的开发者申请和…