zoukankan      html  css  js  c++  java
  • Redis 发布/订阅

    Redis 发布/订阅

    Redis 的发布/订阅功能主要由 PUBLISH,SUBSCRIBE,PSUBSCRIBE 命令组成。

    一个或者多个客户端订阅了某个或者多个频道,当其他客户端向频道发送消息,订阅了该频道的客户端会收到对应的消息。

    这个功能提供两种信息机制,分别为 订阅/发布到频道订阅/发布到模式。

    频道的订阅和信息发送

    Redis 的 subscribe 命令可以订阅一个或多个频道,当有消息发送到被订阅的频道时,消息会发送给所有订阅频道的客户端。

    如下图,client1,client2,client3订阅了频道 channel 。

     当有消息通过publish 命令发送给频道channel时,这个消息会发送给订阅该频道的客户端。

     下面说一下 subscribe 和 publish 的命令的实现,频道发布订阅原理。

    订阅频道

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

    struct redisServer {
        // ...
        dict *pubsub_channels;
        // ...
    };

    其中字典的键为被订阅的频道,值为所有订阅该频道的客户端,是一个链表的结构。

    如下图,客户端client1、client2、client3订阅了频道channel1,客户端client4、client5、client6订阅了频道channel2。

     当一个客户端使用 subscribe命令订阅频道时,程序会把客户端加在字典中频道对应的链表。

    如图,客户端client7 执行命令 subscribe channel2 ,上图则变成下面的图。

     subscribe 命令伪代码表示如下:

    def SUBSCRIBE(client, channels):
    
        # 遍历所有输入频道
        for channel in channels:
    
            # 将客户端添加到链表的末尾
            redisServer.pubsub_channels[channel].append(client)

    发送消息到频道

    调用  publish channel message  命令, 程序首先根据 channel 定位到字典的键, 然后将信息发送给字典值链表中的所有客户端。

    如图,客户端执行命令 publish channel1 "hello world!" ,那么client1、client2、client3 三个客户端都将接收到 "hello world!" 信息:

     

     publish 命令伪代码表示如下:

    def PUBLISH(channel, message):
    
        # 遍历所有订阅频道 channel 的客户端
        for client in server.pubsub_channels[channel]:
    
            # 将信息发送给它们
            send_message(client, message)

    退订频道

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

    模式的订阅和信息发送

    使用publish 命令发送消息给频道时,不仅订阅该频道的客户端会接受到消息,如果某些模式和这个频道匹配,那么所有订阅该模式的客户端也将接受到消息。

    下图表示一个频道和一个模式,模式channel*匹配频道channel。

     当有信息发送到 channel 频道时, 信息除了发送给 client1、client2、client3之外, 还会发送给订阅 channel*模式的client4和client5:

     订阅模式

    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 链表,其中client1和client2都订阅着channle*模式。

     这时客户端 client3 执行命令 psubscribe channel_test*, 那么 pubsub_patterns 链表将被更新成这样:

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

    发送信息到模式

    发送信息到模模式也是 publish 命令,上面提到的 发送信息到频道 只给出了部分代码。实际上 publish 命令将 message 发送到所有订阅 channel 的客户端之外, 它还会将 channel 和 pubsub_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 状态如下:

     那么当消息发送到 channel 的时候,除了订阅channel的客户端会接受到消息,上图的client1和client2也会接受到消息。因为这两个客户端订阅的模式匹配了频道channel。

    退订模式

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

    应用场景

    1 构建实时消息系统,比如普通的即时聊天,群聊等功能

    2 在我们的分布式架构中,常常会遇到读写分离的场景,在写入的过程中,就可以使用redis发布订阅,使得写入值及时发布到各个读的程序中,就保证数据的完整一致性

    3 在一个博客网站中,有100个粉丝订阅了你,当你发布新文章,就可以推送消息给粉丝们

    参考文献

    Redis 设计与实现

    https://www.cnblogs.com/liuqingzheng/p/10009541.html

    https://www.cnblogs.com/yitudake/p/6747995.html

  • 相关阅读:
    ARPPING
    Oracle RAC 连接
    Win7 DCOM 配置中我的电脑出现红色箭头并且无属性显示的解决方法
    Ping命令
    Linux & Oracle 安装目录说明
    TCP 四次握手
    wireshark使用
    jcaptcha组件小小改造解决Invalid ID, could not validate une
    JustSniffer
    java 自己定义异常,记录日志简单说明!留着以后真接复制
  • 原文地址:https://www.cnblogs.com/hulunbao/p/13744180.html
Copyright © 2011-2022 走看看