Redis之釋出與訂閱
釋出與訂閱
Redis的釋出訂閱功能由PUBISH,SUBSCRIBE,PSUBSCRIBE命令組成。
通過執行SUBSCRIBE命令,客戶端可以訂閱一個或者多個頻道,從而成為這些頻道的訂閱者(subscriber):每當有其他客戶端向被訂的頻道傳送訊息時,頻道的所有訂閱者都會收到這條訊息。
除了訂閱頻道之外,客戶端還可以通過執行PSUBSCRIBE命令訂閱一個或者多個模式,從而成為這些模式的訂閱者:每當有其他客戶端向莫格頻道傳送訊息時,訊息不僅會發送給這個頻道的所有訂閱者,還會發送給所有與這個模式相匹配的模式的訂閱者。
- 例如,客戶端A正在訂閱頻道“new.it”。
- 客戶端B正在訂閱頻道“new.et”。
- 客戶端C和客戶端D正在訂閱“new.it”頻道和"new.et"頻道相匹配的模式"new.[ie].t"。
向“new.it"頻道傳送訊息"hello",那麼不僅正在訂閱”new.it“頻道的客戶端A會收到雄安熙,客戶端C和D也會收到訊息,因為這兩個客戶端正在訂閱匹配"new.it"頻道的"new.[ie]t"模式。
頻道的訂閱與退訂
當一個客戶端執行SUBSCRIBE命令訂閱某個或者某些頻道時,這個客戶端與被訂閱頻道之間就建立了一種訂閱關係。
Redis將所有頻道的訂閱關係都儲存在伺服器狀態的pubsub_channels字典裡面,這個字典的鍵是某個被訂閱的頻道,而鍵的值則是一個連結串列,連結串列裡面記錄了所有訂閱這個頻道的客戶端:
struct redisServer {
//儲存所有頻道的訂閱關係
dict *pusub_channels;
};
訂閱頻道
每當客戶端執行SUBSCRIBE命令訂閱某個或者某些頻道時,伺服器都會將客戶端與被訂閱的頻道在pubsub_channels字典進行關聯。
根據頻道頻道是否有其他訂閱者,關聯操作分為兩種情況執行:
- 如果頻道已經有其他訂閱者,那麼它在pubsub_channels字典中必定有相應的訂閱者連結串列,程式唯一要做的就是將客戶端新增到訂閱者連結串列的末尾。
- 如果頻帶還未有任務訂閱者,那麼它必然不存在字典中,程式首先在pubsub_channels字典中為頻道建立一個鍵,並將這個鍵的值設定為空連結串列,然後將客戶都安新增到連結串列,成為連結串列的第一個元素。
退訂頻道
UNSUBSCRIBE命令的行為和SUBSCRIBE命令的行為正好相反,當一個客戶都安退訂某個或者某些頻道時,伺服器將從pubsub_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_pattern連結串列的表尾。
退訂模式
模式的退訂命令PUNSUBSCRIBE是PSUBSCRIBE命令的反操作:
當一個客戶端退訂某個或者某些模式的時候,伺服器將在pubsub_patterns連結串列中查詢並且刪除那些pattern屬性為退訂模式,並且client屬性為執行退訂命令的客戶端的pubsubPattern結構
傳送訊息
當一個Redis客戶端執行PUBLISH命令將訊息message傳送給頻道channel時候,伺服器需要執行以下兩個動作:
- 將訊息message傳送給channel頻道的所有訂閱者
- 如果有一個或者多個模式pattern與頻道channel相匹配,那麼將訊息message傳送給pattern模式的訂閱者
將訊息傳送給頻道訂閱者
因為伺服器狀態中的所有pubsub_channel字典記錄了所有頻道的訂閱關係,所以為了將訊息傳送給channel頻道的所有訂閱者,PUBLISH命令要做的就是在pubsub_channels字典裡面找到頻道channel訂閱者名單(一個連結串列,將訊息傳送給名單上的所有客戶端)
例如,執行以下命令:
PUBLISH ”new.it“ "hello"
那麼PUBLISH命令將在pubsub_channels字典中查詢鍵”new.it“對應的連結串列值,並且通過遍歷連結串列將訊息"hello"傳送給訂閱者
將訊息傳送給模式訂閱者
因為伺服器狀態中的pubsub_patterns連結串列記錄了所有模式的訂閱關係,所以為了將訊息傳送給所有與channel頻道相匹配的模式的訂閱者,PUBLISH命令要做的就i是遍歷整個連結串列,查詢那些channel頻道相匹配的模式,並將訊息傳送給訂閱了這些模式的客戶端。
小結
伺服器狀態在pubsub_channels字典中儲存了所有頻道的訂閱關係:
- SUBSCRIBE命令負責將客戶端與被訂閱的頻道關聯到這個字典中,而UNSUBSCRIBE命令則負責接觸客戶端和被退定的頻道之間的關聯
- 伺服器狀態在pubsub_patterns連結串列儲存了所有模式的訂閱關係:PSUBSCRIBE命令負責將客戶端和被訂閱的模式記錄到這個連結串列中,而PUNSUBSCRIBE命令則負責溢位客戶端與被退訂模式在連結串列中的記錄
- PUBLISH命令通過訪問pubsub_channels字典來向頻道的所有訂閱者傳送訊息,通過訪問pubsub_patterns連結串列來向所有匹配頻道的模式的訂閱者傳送訊息