redis 订阅与发布

Reference: https://redisbook.readthedocs.io/en/latest/feature/pubsub.html

Redis 的 SUBSCRIBE 命令可以让客户端订阅任意数量的频道, 每当有新信息发送到被订阅的频道时, 信息就会被发送给所有订阅指定频道的客户端。

作为例子, 下图展示了频道 channel1 , 以及订阅这个频道的三个客户端 —— client2client5client1 之间的关系:

redis 订阅与发布

当有新消息通过 PUBLISH 命令发送给频道 channel1 时, 这个消息就会被发送给订阅它的三个客户端:

redis 订阅与发布

在后面的内容中, 我们将探讨 SUBSCRIBEPUBLISH 命令的实现, 以及这套订阅与发布机制的运作原理。

订阅频道

每个 Redis 服务器进程都维持着一个表示服务器状态的 redis.h/redisServer 结构, 结构的 pubsub_channels 属性是一个字典, 这个字典就用于保存订阅频道的信息:

struct redisServer {
    // ...

    dict *pubsub_channels;
    // ...

};

其中,字典的键为正在被订阅的频道, 而字典的值则是一个链表, 链表中保存了所有订阅这个频道的客户端。

比如说,在下图展示的这个 pubsub_channels 示例中, client2client5client1 就订阅了 channel1 , 而其他频道也分别被别的客户端所订阅:

redis 订阅与发布

当客户端调用 SUBSCRIBE 命令时, 程序就将客户端和要订阅的频道在 pubsub_channels 字典中关联起来。

举个例子,如果客户端 client10086 执行命令 <span class="pre">SUBSCRIBE&#xA0;<span class="pre">channel1&#xA0;<span class="pre">channel2&#xA0;<span class="pre">channel3</span></span></span></span> ,那么前面展示的 pubsub_channels 将变成下面这个样子:

redis 订阅与发布

SUBSCRIBE 命令的行为可以用伪代码表示如下:

def SUBSCRIBE(client, channels):

    # 遍历所有输入频道
    for channel in channels:

        # 将客户端添加到链表的末尾
        redisServer.pubsub_channels[channel].append(client)

通过 pubsub_channels 字典, 程序只要检查某个频道是否为字典的键, 就可以知道该频道是否正在被客户端订阅; 只要取出某个键的值, 就可以得到所有订阅该频道的客户端的信息。

发送信息到频道

了解了 pubsub_channels 字典的结构之后, 解释 PUBLISH 命令的实现就非常简单了: 当调用 <span class="pre">PUBLISH&#xA0;<span class="pre">channel&#xA0;<span class="pre">message</span></span></span> 命令, 程序首先根据 channel 定位到字典的键, 然后将信息发送给字典值链表中的所有客户端。

比如说,对于以下这个 pubsub_channels 实例, 如果某个客户端执行命令 <span class="pre">PUBLISH&#xA0;<span class="pre">channel1&#xA0;<span class="pre">"hello&#xA0;<span class="pre">moto"</span></span></span></span> ,那么 client2client5client1 三个客户端都将接收到 <span class="pre">"hello&#xA0;<span class="pre">moto"</span></span> 信息:

redis 订阅与发布

PUBLISH 命令的实现可以用以下伪代码来描述:

def PUBLISH(channel, message):

    # 遍历所有订阅频道 channel 的客户端
    for client in server.pubsub_channels[channel]:

        # 将信息发送给它们
        send_message(client, message)

退订频道

使用 UNSUBSCRIBE 命令可以退订指定的频道, 这个命令执行的是订阅的反操作: 它从 pubsub_channels 字典的给定频道(键)中, 删除关于当前客户端的信息, 这样被退订频道的信息就不会再发送给这个客户端。

模式的订阅与信息发送

当使用 PUBLISH 命令发送信息到某个频道时, 不仅所有订阅该频道的客户端会收到信息, 如果有某个/某些模式和这个频道匹配的话, 那么所有订阅这个/这些频道的客户端也同样会收到信息。

下图展示了一个带有频道和模式的例子, 其中 tweet.shop.* 模式匹配了 tweet.shop.kindle 频道和 tweet.shop.ipad 频道, 并且有不同的客户端分别订阅它们三个:

redis 订阅与发布

当有信息发送到 tweet.shop.kindle 频道时, 信息除了发送给 clientXclientY 之外, 还会发送给订阅 tweet.shop.* 模式的 client123client256

redis 订阅与发布

另一方面, 如果接收到信息的是频道 tweet.shop.ipad , 那么 client123client256 同样会收到信息:

redis 订阅与发布

订阅模式

redisServer.pubsub_patterns 属性是一个链表,链表中保存着所有和模式相关的信息:

struct redisServer {
    // ...

    list *pubsub_patterns;
    // ...

};

链表中的每个节点都包含一个 redis.h/pubsubPattern 结构:

typedef struct pubsubPattern {
    redisClient *client;
    robj *pattern;
} pubsubPattern;

client 属性保存着订阅模式的客户端,而 pattern 属性则保存着被订阅的模式。

每当调用 PSUBSCRIBE 命令订阅一个模式时, 程序就创建一个包含客户端信息和被订阅模式的 pubsubPattern 结构, 并将该结构添加到 redisServer.pubsub_patterns 链表中。

作为例子,下图展示了一个包含两个模式的 pubsub_patterns 链表, 其中 client123client256 都正在订阅 tweet.shop.* 模式:

redis 订阅与发布

如果这时客户端 client10086 执行 <span class="pre">PSUBSCRIBE&#xA0;<span class="pre">broadcast.list.*</span></span> , 那么 pubsub_patterns 链表将被更新成这样:

redis 订阅与发布

通过遍历整个 pubsub_patterns 链表,程序可以检查所有正在被订阅的模式,以及订阅这些模式的客户端。

发送信息到模式

发送信息到模式的工作也是由 PUBLISH 命令进行的, 在前面讲解频道的时候, 我们给出了这样一段伪代码, 说它定义了 PUBLISH 命令的行为:

def PUBLISH(channel, message):

    # 遍历所有订阅频道 channel 的客户端
    for client in server.pubsub_channels[channel]:

        # 将信息发送给它们
        send_message(client, message)

但是,这段伪代码并没有完整描述 PUBLISH 命令的行为, 因为 PUBLISH 除了将 message 发送到所有订阅 channel 的客户端之外, 它还会将 channelpubsub_patterns 中的模式进行对比, 如果 channel 和某个模式匹配的话, 那么也将 message 发送到订阅那个模式的客户端。

完整描述 PUBLISH 功能的伪代码定于如下:

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)

举个例子,如果 Redis 服务器的 pubsub_patterns 状态如下:

redis 订阅与发布

那么当某个客户端发送信息 <span class="pre">"Amazon&#xA0;<span class="pre">Kindle,&#xA0;<span class="pre">$69."</span></span></span>tweet.shop.kindle 频道时, 除了所有订阅了 tweet.shop.kindle 频道的客户端会收到信息之外, 客户端 client123client256 也同样会收到信息, 因为这两个客户端订阅的 tweet.shop.* 模式和 tweet.shop.kindle 频道匹配。

退订模式

使用 PUNSUBSCRIBE 命令可以退订指定的模式, 这个命令执行的是订阅模式的反操作: 程序会删除 redisServer.pubsub_patterns 链表中, 所有和被退订模式相关联的 pubsubPattern 结构, 这样客户端就不会再收到和模式相匹配的频道发来的信息。

小结

  • 订阅信息由服务器进程维持的 redisServer.pubsub_channels 字典保存,字典的键为被订阅的频道,字典的值为订阅频道的所有客户端。
  • 当有新消息发送到频道时,程序遍历频道(键)所对应的(值)所有客户端,然后将消息发送到所有订阅频道的客户端上。
  • 订阅模式的信息由服务器进程维持的 redisServer.pubsub_patterns 链表保存,链表的每个节点都保存着一个 pubsubPattern 结构,结构中保存着被订阅的模式,以及订阅该模式的客户端。程序通过遍历链表来查找某个频道是否和某个模式匹配。
  • 当有新消息发送到频道时,除了订阅频道的客户端会收到消息之外,所有订阅了匹配频道的模式的客户端,也同样会收到消息。
  • 退订频道和退订模式分别是订阅频道和订阅模式的反操作。

Original: https://www.cnblogs.com/skying555/p/11330308.html
Author: alex.shu
Title: redis 订阅与发布

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/529244/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

  • Linux磁盘管理

    对Linux来说一切皆文件,Linux归根结底只有一个根目录,一个独立且唯一的文件结构,Linux的每个分区都是用来组成整个文件系统的一部分。所以Linux采用了磁盘挂载的方式,将…

    Linux 2023年6月8日
    096
  • 顺序栈

    #pragma once #ifndef _SEQSTACK_ #define _SEQSTACK_ #include using namespace std; typedef i…

    Linux 2023年6月7日
    074
  • Redis6.0.5版本配置文件说明(转载)

    Redis版本此文章中Redis版本为6.0.5。 redis-server –versionRedis server v=6.0.5 sha=00000000:0 m…

    Linux 2023年5月28日
    092
  • kubeadm 加入新master 报错

    error execution phase check-etcd: error syncing endpoints with etcd: context deadline exce…

    Linux 2023年6月14日
    0116
  • 解决“WARNINGThe remote SSH server rejected X11 forwarding request.“警告

    使用xshell连接服务器时,出现了”WARNING! The remote SSH server rejected X11 forwarding request.&#…

    Linux 2023年5月27日
    079
  • tar压缩提示file changed as we read it

    压缩文件夹,过程中某个文件有变化,会提示 file changed as we read it 。不太确定是压缩到这里就中断了,还是压缩完,才提示的这个错误。 测试一下 做个实验,…

    Linux 2023年6月8日
    0160
  • PHP array_reduce()

    array_reduce array_reduce() 将回调函数 callback 迭代地作用到 array 数组中的每一个单元中,从而将数组简化为单一的值。 示例一: 示例二:…

    Linux 2023年6月7日
    099
  • 1s 创建100G文件,最快的方法是?

    在我们日常工作中,为了验证开发的功能,比如:文件上传功能或者算法的处理效率等,经常需要一些大文件进行测试,有时在四处找了一顿之后,发现竟然没有一个合适的,虽然 Linux 中也有一…

    Linux 2023年5月27日
    075
  • 对脱壳脚本的一些改进–识别出目标DEX

    一、前言 通常对于加壳的程序,第一步的操作通常是脱壳,而现在脱壳一般都选择利用 frida 来进行 hook 进行脱壳,不谈其他脱壳方式,利用 frida 脱壳原理大致分为两种: …

    Linux 2023年6月8日
    094
  • 上篇:34个JavaScript栗子,从易到难。

    alert("hello world") document.write("hello world") console.log("好…

    Linux 2023年6月7日
    077
  • JavaScript快速入门-02-基本语法

    2 基本语法 2.1 JavaScript简介 JavaScript 是一门 解释型语言,其代码在客户端中执行前不需经过编译,而是直接由浏览器解释执行。主要用作 客户端脚本语言,在…

    Linux 2023年6月7日
    0103
  • docker网络管理

    服务器版本 docker软件版本 CPU架构 CentOS Linux release 7.4.1708 (Core) Docker version 20.10.12 x86_64…

    Linux 2023年6月7日
    078
  • 课间游戏志:斗荧光笔与扒撸咔嚓

    课间游戏志:斗荧光笔与扒撸咔嚓 写这篇博客,主要是想记录两个课间游戏,一个是我于小学四年级时发明的斗荧光笔,一个是初中时班上几个变态发明的扒撸咔嚓,自从这两个游戏被发明以后,我们班…

    Linux 2023年6月6日
    087
  • Running powershell scripts during nuget package installation and removal

    来源:https://devblogs.microsoft.com/nuget/NuGet-3-What-and-Why/ Since Visual Studio 2015 was…

    Linux 2023年5月28日
    092
  • Linux下如何切割与合并大文件

    我们传输一个大文件时,有时网络比较慢,需要花费很长时间才能传输完成,或者传输的过程中,网络不稳定,有可能导致此次传输失败,针对这种情况,我们可以把大文件切分成小文件,再逐个的传输到…

    Linux 2023年6月13日
    0112
  • vue指令——day01

    v-cloak:能够解决插值表达式闪烁的问题 <p v-cloak>{{ msg }}</p> v-text:会覆盖元素中原本的内容,但是插值表达式 只会替…

    Linux 2023年6月7日
    0102
亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球