Redis pubsub机制源码分析

Redis的SUBSCRIBE命令,可以让客户端订阅任意数量的频道,每当有新消息发送到某个频道时,Redis就会把这消息发送给所有订阅该频道的客户端;如下图:客户端Client_1,Client_2,Client_3都订阅了频道channel,当有消息PUBLISH到频道channel时,这三个客户端都将收到消息: 

 

原理 :RedisServer内部维护了一个pubsub_channels字典,其中字典的键就是被订阅的频道,而键值就是订阅该频道的客户端列表; 

 

这样,当一个客户端执行PUBLISH channel_name命令时,Redis就可以根据channel_name在pubsub_channels中找到与其关联的客户端列表,然后把消息发送给它们,伪代码; 
Python代码   收藏代码
  1. def publishCommand(channel, msg):  
  2.     # 获取订阅channel的所有客户端列表  
  3.     client_list = redisServer.pubsub_channels.get(channel)  
  4.     if client_list is Nonereturn  
  5.     # 向每个客户端发送消息  
  6.     for client in client_list:  
  7.         client.sendMessage(msg)  

另外,客户端自己也维护了一个pubsub_channels属性,用来记录自己订阅了哪些频道;同watched_keys属性一样(详情请参看  事务 章节),客户端维护这些也是出于效率考虑的: 

a. 防止订阅相同的频道; 
Python代码   收藏代码
  1. def subscribeCommand(client, channels):  
  2.     for ch in channels:  
  3.         # 如果已经订阅了该频道,则跳过  
  4.         if ch  in client.pubsub_channels: continue  
  5.         # 把client添加到该频道关联的客户端列表  
  6.         client_list = redisServer.pubsub_channels.get(ch)  
  7.         client_list.add(client)   
  8.         client.pubsub_channels.add(ch)  

b. 在UNSUBSCRIBE时,可以快捷的取消该客户端订阅的所有频道,而无需遍历整个redisServer.pubsub_channels字典,伪代码: 
Python代码   收藏代码
  1. def unsubscribeCommand(client):  
  2.     # 获取client订阅的所有频道  
  3.     channels = client.pubsub_channels  
  4.     # 遍历频道  
  5.     for ch in channels:  
  6.         client_list = redisServer.pubsub_channels(ch)    
  7.         # 从该频道关联的客户端列表中,删除client  
  8.         client_list.del(client)  
  9.         client.pubsub_channels.del(ch)  


考虑这么一个需求:有两个频道,名字都以“hello_开头”,分别叫做hello_1, hello_2;当我们要订阅这类频道时,我们可能会这么写:SUBSCRIBE hello_1 hello_2,但是如果有100个难道要这样写 SUBSCRIBE hello_1 hello_2 ... hello_100? 这时候我们可以使用“ 模式订阅 ”命令PSUBSCRIBE, 譬如这里我们就可以写成,PSUBSCRIBE hello_* ;这样,当一个客户端执行PUBLISH命令时,redis不仅会把消息发送给所有订阅该频道的客户端列表,同时也会把该频道与所有模式匹配,如果匹配成功,则把消息同样发送给订阅该模式的客户端列表: 

 

所以完整的PUBLISH命令伪代码如下: 
Python代码   收藏代码
  1. def publishCommand(channel, msg):  
  2.     # 获取订阅channel的所有客户端列表  
  3.     client_list = redisServer.pubsub_channels.get(channel)  
  4.     if client_list is Nonereturn  
  5.     # 向每个客户端发送消息  
  6.     for client in client_list:  
  7.         client.sendMessage(msg)  
  8.   
  9.     # 遍历pubsub_patterns  
  10.     for pattern, client in redisServer.pubsub_patterns:  
  11.         # 若模式与channel匹配,则把消息发送给订阅该模式的客户端  
  12.         if pattern.match(channel):  
  13.              client.sendMessage(msg)  

更多细节请看:pubsub.c/publishCommand函数 

总结:  
1. 熟悉发布订阅相关命令:subscribe/unsubscribe psubscribe/punsubscribe publish; 

2. 了解发布订阅实现原理; 

转自:http://diaocow.iteye.com/blog/1935094

©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页