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=clientpubsubPattern.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?鏈表中的信息來實現的。
總結
以上是生活随笔為你收集整理的redis——发布和订阅的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: leetcode217. 存在重复元素(
- 下一篇: online游戏服务器架构--网络架构