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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章