當一個客戶端執行SUBSCRIBE命令訂閱某個或某些頻道時,這個客戶端與被訂閱頻道之間就建立起了一種訂閱關係。
Redis將所有頻道的訂閱關係儲存在伺服器狀態的pubsub_channels字典裡面,這個字典的鍵是某個被訂閱的頻道,而鍵的值是一個連結串列,連結串列裡面記錄了所有訂閱這個頻道的客戶端:
struct redisServer{ //儲存所有頻道的訂閱關係 dict *pubsub_channels; }
如果頻道已經有其他的訂閱者,那麼他在pubsub_channels字典中必然有相應的訂閱者連結串列,程式唯一要做的就是將客戶端新增到訂閱者連結串列的末尾,反之程式首先要在pubsub_channels字典中為頻道建立一個鍵,並將這個鍵的值設定為空連結串列,然後將客戶端新增到連結串列,成為連結串列的第一個元素。
UNSUBSCRIBE命令的行為與SUBSCRIBE命令相反,當一個客戶端推定某個或某些頻道的時候,伺服器將從pubsub_channels中接觸客戶端與被訂閱頻道之間的關聯:程式會根據被推定頻道的名字,在publish_channels字典中找到頻道對應的訂閱連結串列,而後從訂閱者連結串列中刪除退訂客戶端的資訊;如果刪除推定客戶端之後,頻道的訂閱者連結串列變成空連結串列,那麼說明頻道已經沒有了訂閱者了,程式將從pubsub_channels字典中刪除頻道對應的鍵。
伺服器將所有頻道的訂閱關係都儲存在伺服器狀態的pubsub_channels屬性裡面,與之相似,伺服器也將所有模式的訂閱關係都儲存在伺服器狀態的pubsub_patterns屬性裡面:
struct redisServer { //儲存所有訂閱關係 list *pubsub_patterns; };
pubsub_patterns屬性是一個連結串列,連結串列中的每個節點都包含著一個pubsubpattern結構,這個結構的pattern屬性記錄了被訂閱的模式,而client屬性則記錄了訂閱模式的客戶端:
typedef struct pubsubPattern{ //訂閱模式的客戶端 redisClient *client; //被訂閱的模式 robj *pattern; } pubsubPattern;
訂閱模式
每當客戶端執行PSUBSCRIBE命令訂閱某個或某些模式的時候,伺服器會對某個被訂閱的模式執行:新建一個pubsubPattern結構,將結構的pattern屬性設定為被訂閱的模式,client屬性設定為訂閱模式的客戶端;將pubsubPattern結構新增到pubsub_patterns連結串列的表尾。
傳送訊息
當一個Redis客戶端執行PUBLISH < channel > < message > 命令將訊息message傳送給頻道channel的時候,伺服器需要執行以下兩個動作:將訊息message傳送給channel頻道的所有訂閱者;如果有一個或多個模式pattern與頻道channel相匹配,那麼將訊息message傳送給pattern模式的訂閱者。
因為伺服器狀態中pubsub_channels字典記錄了所有頻道的訂閱關係,所以為了將訊息傳送給channel頻道的所有訂閱者,PUBLISH命令要做的就是在subsub_channels字典裡找到頻道channel的訂閱者名單(連結串列),然後將訊息傳送給名單上的所有客戶端。
將訊息傳送欸模式訂閱者,因為伺服器狀態中的pubsub_patterns連結串列記錄了所有模式的訂閱關係,所以為了將訊息傳送給所有與channel頻道相匹配的模式的訂閱者,PUBLISH命令要做的就是遍歷整個pubsub_patterns連結串列,查詢那些與channel頻道相匹配的模式,並將訊息傳送給訂閱了謝謝模式的客戶端。
PUBSUB CHANNELS 【pattern】子命令用於返回伺服器當前被訂閱的頻道,如果【pattern】引數不給定,那麼命令返回伺服器當前被訂閱的所有頻道。
PUBSUB NUMSUB 子命令接收任意多個頻道作為輸入引數,並返回這些頻道的訂閱者數量。
PUBSUB NUMPAT子命令用於返回伺服器當前訂閱模式的數量
每天學一點,總會有收穫。
說明:尊重作者智慧財產權,文中內容參考《Redis設計與實現》,僅在此做學習與大家分享。