1. 程式人生 > 資料庫 >Redis之釋出與訂閱

Redis之釋出與訂閱

歡迎閱讀大魔王的睡前私語系列,這是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連結串列來向所有匹配頻道的模式的訂閱者傳送訊息