Redis系列22:Redis 的Pub/Sub能力

2023-09-04 15:00:27

Redis系列1:深刻理解高效能Redis的本質
Redis系列2:資料持久化提高可用性
Redis系列3:高可用之主從架構
Redis系列4:高可用之Sentinel(哨兵模式)
Redis系列5:深入分析Cluster 叢集模式
追求效能極致:Redis6.0的多執行緒模型
追求效能極致:使用者端快取帶來的革命
Redis系列8:Bitmap實現億萬級資料計算
Redis系列9:Geo 型別賦能億級地圖位置計算
Redis系列10:HyperLogLog實現海量資料基數統計
Redis系列11:記憶體淘汰策略
Redis系列12:Redis 的事務機制
Redis系列13:分散式鎖實現
Redis系列14:使用List實現訊息佇列
Redis系列15:使用Stream實現訊息佇列
Redis系列16:聊聊布隆過濾器(原理篇)
Redis系列17:聊聊布隆過濾器(實踐篇)
Redis系列18:過期資料的刪除策略
Redis系列19:LRU記憶體淘汰演演算法分析
Redis系列20:LFU記憶體淘汰演演算法分析
Redis系列21:快取與資料庫的資料一致性討論

1 關於Redis 的 Pub/Sub

Redis的釋出訂閱(Pub/Sub)模式是一種訊息傳遞機制,它允許在傳送者和接收者之間建立鬆耦合的通訊關係。在這種模式中,傳送者(釋出者)將訊息釋出到一個指定的頻道或模式,而接收者(訂閱者)可以訂閱一個或多個頻道,以便接收發布的訊息。
以下是Redis釋出訂閱模式的主要元件:
釋出者(Publisher):釋出者是產生並行布訊息的實體。它可以將訊息傳送到指定的頻道或模式。
訂閱者(Subscriber):訂閱者是接收並處理訊息的實體。它可以訂閱一個或多個頻道或模式,以便接收相關的訊息。
頻道(Channel):頻道是釋出者和訂閱者之間的通訊渠道。釋出者將訊息傳送到頻道,而訂閱者從頻道接收訊息。

可以看下圖,Publisher 和 Subscriber、Channel的關係很清晰:

釋出者往 "Channel A" 通道釋出訊息:Hello World!,訊息的所有訂閱者就會收到這個訊息。

2 使用Pub/Sub實現釋出訂閱的過程

Redis實現 釋出/訂閱 一共有兩種模式:

  • 使用頻道(Channel)進行釋出訂閱
  • 使用模式(Pattern)進行釋出訂閱

我們知道,Redis 可以支援多個資料庫,每個資料庫都有自己的名稱空間和資料。通過使用多個資料庫,可以實現資料隔離、分割區和組織。
但是值得注意的是,這種釋出訂閱機制與 資料分割區空間無關,比如在 db 0 釋出訊息, 其他區的訂閱者都會收到訊息。

2.1 通過頻道(Channel)進行釋出訂閱

  • 首先,Subscriber訂閱某個Channel,實現對Channel的監聽
  • Publisher 對 Channel 這個服務中心媒介釋出訊息
  • 所有訂閱 Channel 的Subscriber接收到訊息

接下來我們通過這幾個步驟看看具體是怎麼實現釋出與訂閱的過程的。

2.1.1 訂閱者訂閱頻道

SUBSCRIBE命令:訂閱者使用SUBSCRIBE命令訂閱一個或多個頻道。語法為SUBSCRIBE channel [channel ...]。
時間複雜度為O(n) ,n 為訂閱的 Channel 數量。

SUBSCRIBE mychannel
Reading messages... (press Ctrl-C to quit)
1) "subscribe" // Message Type
2) "mychannel" // channel
3) (integer) 1 

執行完該指令後,訂閱者可以使用以下命令操作 Pub/Sub 工作:

  • subscribe:訂閱
  • unsubscribe:取消訂閱
  • psubscribe:訂閱者使用PSUBSCRIBE命令訂閱一個或多個模式
  • punsubscribe:訂閱者使用PUNSUBSCRIBE命令取消訂閱一個或多個模式

我們使用使用者端 [subscriber A] 訂閱Channel [mychannel] 來接收訊息。從上面可以看出響應的資訊:

  • "subscribe" :訊息型別,列舉是 subscribe、message、unsubscribe
  • "mychannel" :頻道的名稱
  • 最後的訊息內容:不同的訊息型別代表不同含義。

進入訂閱後的使用者端可以收到 3 種列舉型別的訊息:

  • subscribe:訂閱成功的訊息型別,第2個值是訂閱成功的頻道名稱,第3個值是當前使用者端訂閱的頻道數量。(參考我們上面的mychannel訊息)
  • message:使用者端接收訊息的訊息型別,第2個值表示產生訊息的頻道名稱,第3個值是訊息的內容。
  • unsubscribe:取消訂閱的訊息型別,第2個值是對應的頻道名稱,第3個值是當前使用者端訂閱的頻道數量。值為 0 時說明使用者端一個訂閱的都沒有了,退出訂閱狀態。

2.1.2 釋出者釋出訊息

PUBLISH命令:釋出者使用PUBLISH命令將訊息傳送到指定的頻道。語法為 PUBLISH channel message

PUBLISH mychannel "Hello, World!"
(integer) 1

需要注意咱們釋出的訊息並不會持久化儲存下來,所以訊息釋出之後被某個 Subcriber 訂閱到的話,訊息生命週期基本就完成了。

2.1.3 訂閱者接收訊息

如果想要收到上面 釋出者釋出的訊息,我們的使用者端首先需要關注了 [mychannel] 頻道,才能收到 "Hello, World!" 這條訊息。

// 第一步,訂閱Channel頻道
SUBSCRIBE mychannel
Reading messages... (press Ctrl-C to quit)
1) "subscribe" // 訂閱成功
2) "mychannel" // 頻道名稱
3) (integer) 1  // 訂閱頻道數量

// 第二步,當Publisher釋出訊息,Subcriber訂閱到的訊息如下
1) "message" // 接受到訊息
2) "mychannel" // 頻道名稱
3) "Hello, World!" // 訊息內容

2.1.4 退訂頻道

如果你不想收到某個頻道的訊息了,你可以取消預訂。類似取消朋友圈關注,之後就不會收到推播了。
UNSUBSCRIBE命令:訂閱者使用UNSUBSCRIBE命令取消訂閱一個或多個頻道。語法為 UNSUBSCRIBE channel [channel ...]

UNSUBSCRIBE mychannel

2.2 使用模式(Pattern)匹配實現釋出訂閱

我們來看看另一種實現釋出訂閱的方案 ,就是模式匹配的方式,除了直接訂閱的使用者端之外,還會檢查是否有與我們模式相匹配的Channel,如果有,
訊息也會發布到對應匹配的頻道上,訂閱這個Channel的使用者端也會收到訊息。
我們來試試看效果,訂閱匹配模式如下圖:

當 Message.Queue.Area1 頻道接收到訊息之後,除了訂閱自身頻道的 Actor A 和 Actor B 能收到訊息之外。因為頻道與模式匹配成功,訊息還會傳送給訂閱 Message.Queue.* 模式的所有人員。

如上面圖中的那樣,因為使用匹配模式,PUBLISH 訊息釋出到 Message.Queue.Area2 之外,還會將該 Channel 與匹配模式的Channel進行對比,如果 Channel 與某個模式匹配的話,也將這個訊息釋出到訂閱這個模式的使用者端。
所以中紅色線條部分,包括Actor C、Actor D、Actor E 都接受到了訊息。

2.2.1 訂閱模式的相關語法

  • 訂閱模式指令:PSUBSCRIBE
PSUBSCRIBE Message.Queue.*
Reading messages... (press Ctrl-C to quit)
1) "psubscribe" // 訊息型別:使用模式(Pattern)進行釋出訂閱
2) "Message.Queue.*"// 匹配的模式
3) (integer) 1 //訂閱數
  • 取消模式訂閱的指令:PUNSUBSCRIBE
PUNSUBSCRIBE Message.Queue.*
  • 訂閱 Message.Queue.Area1 和 Message.Queue.Area2 頻道的
# Message.Queue.Area1
SUBSCRIBE Message.Queue.Area1
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "Message.Queue.Area1"
3) (integer) 1

# Message.Queue.Area2
SUBSCRIBE Message.Queue.Area2
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "Message.Queue.Area2"
3) (integer) 1
  • Publisher向Message.Queue.Area2釋出訊息
# 匹配模式的訂閱者收到訊息
PSUBSCRIBE Message.Queue.*
Reading messages... (press Ctrl-C to quit)
1) "psubscribe"
2) "Message.Queue.*"
3) (integer) 1
# 進入訂閱狀態,接收到訊息
1) "pmessage"  # 訊息型別
2) "Message.Queue.*" # 模式匹配
3) "Message.Queue.Area2" # 匹配的Channel
4) "Hello World!" # 具體的訊息訊息內容


# 對應頻道的訂閱者收到訊息
SUBSCRIBE Message.Queue.Area2
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "Message.Queue.Area2"
3) (integer) 1
# 準備接收訊息
1) "message"
2) "Message.Queue.Area2"
3) "Hello World!"

因為沒有篩重策略,所以如果你既訂閱了匹配模式(如 Message.Queue.* ),又訂閱了對應的頻道(如 Message.Queue.Area2),那麼你的使用者端會收到兩條同樣的訊息,一條訊息型別是message,一條型別是pmessage。

2.3 程式實現

參考官方程式碼:

# 訊息消費程式碼,監聽頻道
RShardedTopic topic = redisson.getShardedTopic("myTopic");
int listenerId = topic.addListener(SomeObject.class, new MessageListener<SomeObject>() {
    @Override
    public void onMessage(String channel, SomeObject message) {
        //...
    }
});

# 訊息生產程式碼
// in other thread or JVM(釋出訊息與監聽訊息要執行在不同的 JVM,因為同一個redisonClinet,無法監聽到自己的訊息)
RShardedTopic topic = redisson.getShardedTopic("myTopic");
long clientsReceivedMessage = topic.publish(new SomeObject());

3 總結

Redis 實現釋出訂閱的功能,包括如下指令:

  • subscribe:訂閱
  • unsubscribe:取消訂閱
  • psubscribe:訂閱者使用PSUBSCRIBE命令訂閱一個或多個模式
  • punsubscribe:訂閱者使用PUNSUBSCRIBE命令取消訂閱一個或多個模式
  • publish channel message:向指定的頻道channel傳送訊息message

Redis實現 釋出/訂閱 一共有兩種模式:

  • 使用頻道(Channel)進行釋出訂閱
  • 使用模式(Pattern)進行釋出訂閱

需要注意的是,當使用Pattern進行釋出訂閱的時候。如果有訊息釋出出來,除了訂閱該Channel的Client之外,所有訂閱了與Channel匹配的模式的Client同樣會收到訊息。
另外,Redis 釋出訂閱的訊息不會被持久化,所以無歷史訊息,也不支援 ACK 機制,與之前介紹過的 List 與 Stream 訊息佇列能力是不同的,大家注意區分,在不同的場景下合理使用。