redis——发布和订阅
频道的订阅和退订
当一个客户端执行 SUBSCRIBE 命令, 订阅某个或某些频道的时候, 这个客户端与被订阅频道之间就建立起了一种订阅关系。
Redis 将所有频道的订阅关系都保存在服务器状态的 pubsub_channels 字典里面, 这个字典的键是某个被订阅的频道, 而键的值则是一个链表, 链表里面记录了所有订阅这个频道的客户端:
  
   - 
    
     
    
    
     
      struct redisServer {
     
    
 
   - 
    
     
    
    
      
     
    
 
   - 
    
     
    
    
      // ...
     
    
 
   - 
    
     
    
    
      
     
    
 
   - 
    
     
    
    
      // 保存所有频道的订阅关系
     
    
 
   - 
    
     
    
    
     
       dict *pubsub_channels;
     
    
 
   - 
    
     
    
    
      
     
    
 
   - 
    
     
    
    
      // ...
     
    
 
   - 
    
     
    
    
      
     
    
 
   - 
    
     
    
    
     
      };
     
    
 
  
 

每当客户端执行 SUBSCRIBE 命令, 订阅某个或某些频道的时候, 服务器都会将客户端与被订阅的频道在 pubsub_channels 字典中进行关联。
根据频道是否已经有其他订阅者, 关联操作分为两种情况执行:
- 如果频道已经有其他订阅者, 那么它在 
pubsub_channels字典中必然有相应的订阅者链表, 程序唯一要做的就是将客户端添加到订阅者链表的末尾。 - 如果频道还未有任何订阅者, 那么它必然不存在于 
pubsub_channels字典, 程序首先要在pubsub_channels字典中为频道创建一个键, 并将这个键的值设置为空链表, 然后再将客户端添加到链表, 成为链表的第一个元素。 
SUBSCRIBE 命令的实现可以用以下伪代码来描述:
  
   - 
    
     
    
    
     
      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 中解除客户端与被退订频道之间的关联:
- 程序会根据被退订频道的名字, 在 
pubsub_channels字典中找到频道对应的订阅者链表, 然后从订阅者链表中删除退订客户端的信息。 - 如果删除退订客户端之后, 频道的订阅者链表变成了空链表, 那么说明这个频道已经没有任何订阅者了, 程序将从 
pubsub_channels字典中删除频道对应的键。 
UNSUBSCRIBE 命令的实现可以用以下伪代码来描述:
  
   - 
    
     
    
    
     
      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属性里。
  
   - 
    
     
    
    
     
      struct redisServer {
     
    
 
   - 
    
     
    
    
      
     
    
 
   - 
    
     
    
    
      // ...
     
    
 
   - 
    
     
    
    
      
     
    
 
   - 
    
     
    
    
      // 保存所有频道的订阅关系
     
    
 
   - 
    
     
    
    
      list *pubsub_patterns;
     
    
 
   - 
    
     
    
    
      
     
    
 
   - 
    
     
    
    
      // ...
     
    
 
   - 
    
     
    
    
      
     
    
 
   - 
    
     
    
    
     
      };
     
    
 
  
 
pubsub_Patterns属性是一个链表,每个结点是被订阅的模式,节点内记录了模式,节点内的client属性记录了订阅模式的客户端。
  
   - 
    
     
    
    
     
      typedef struct pubsubPattern{
     
    
 
   - 
    
     
    
    
      //订阅模式的客户端
     
    
 
   - 
    
     
    
    
     
       redisClient *client;
     
    
 
   - 
    
     
    
    
      //被订阅的模式
     
    
 
   - 
    
     
    
    
     
       robj *pattern;
     
    
 
   - 
    
     
    
    
     
      }pubsubPattern;
     
    
 
  
 
每当客户端执行PSUBSCRIBE这个命令来订阅某个或某些模式时,服务器会对每个被订阅的模式执行下面的操作:
1)新建一个pubsubPattern结构,设置好两个属性
2)将新节点加到pubsub_patterns尾部
伪代码实现:
  
   - 
    
     
    
    
     
      def osubscribe(*all_input_patterns):
     
    
 
   - 
    
     
    
    
      #遍历所有输入的模式
     
    
 
   - 
    
     
    
    
      #记录被订阅的模式和对应的客户端
     
    
 
   - 
    
     
    
    
     
       pubsubPattern=create()
     
    
 
   - 
    
     
    
    
     
       pubsubPattern.client=client
     
    
 
   - 
    
     
    
    
     
       pubsubPattern.pattern=pattern
     
    
 
   - 
    
     
    
    
      
     
    
 
   - 
    
     
    
    
      #插入链表末尾
     
    
 
   - 
    
     
    
    
     
       server.pub_patterns.append(pubsubPattern)
     
    
 
  
 
模式退订命令PUNSUBSCRIBE是PSUBSCRIBE的反操作
服务器将找到并删除那些被退订的模式
伪代码如下:(我想吐槽一下这样时间复杂度。。。没有更好的办法吗?)
  
   - 
    
     
    
    
     
      def osubscribe(*all_input_patterns):
     
    
 
   - 
    
     
    
    
      #遍历所有退订的模式
     
    
 
   - 
    
     
    
    
      for pattern in all_input_patterns:
     
    
 
   - 
    
     
    
    
      #遍历每一个节点
     
    
 
   - 
    
     
    
    
      for pubsubPattern in server.pubsub_patterns:
     
    
 
   - 
    
     
    
    
      #如果客户端和模式都相同
     
    
 
   - 
    
     
    
    
      if client==pubsubPattern.client:
     
    
 
   - 
    
     
    
    
      if pattern==pubsubPattern.pattern:
     
    
 
   - 
    
     
    
    
      #删除
     
    
 
   - 
    
     
    
    
     
       server.pub_patterns.remove(pubsubPattern)
     
    
 
  
 
发送消息
当一个客户端执行PUBLISH<channel> <message>命令将消息发送给频道时,服务器需要:
1)把消息发送给所有本频道的订阅者
具体做法就是去pubsub_channels字典找到本频道的链表,也就是订阅名单,然后发消息
2)将消息发给,包含本频道的所有模式中的所有订阅者
具体做法就是去pubsub_patterns查找包含本频道的模式,并且把消息发送给订阅它们的客户端。
查看订阅信息
redis2.8新增三个命令,用来查看频道和模式的相关信息。
PUBLISH CHANNELS[pattern]用于返回服务器当前被订阅的频道,pattern可写可不写,不写就查看所有,否则查看与pattern匹配的对应频道
这个子命令是通过遍历pubsub_channels字典实现的。
PUBLISH NUMSUB[CHANNEL-1 CHANNEL-2.....]返回这些频道的订阅者数量
这个子命令是通过遍历pubsub_channels字典,查看对应链表长度实现的。
PUBLISH NUMPAT返回被订阅模式数量
这个子命令是通过返回pubsub_patterns的长度实现的。
总而言之,PUBSUB 命令的三个子命令都是通过读取 pubsub_channels 字典和 pubsub_patterns 链表中的信息来实现的。
文章来源: fantianzuo.blog.csdn.net,作者:兔老大RabbitMQ,版权归原作者所有,如需转载,请联系作者。
原文链接:fantianzuo.blog.csdn.net/article/details/102482618
- 点赞
 - 收藏
 - 关注作者
 
            
           
评论(0)