Redis系列(八):釋出與訂閱
阿新 • • 發佈:2020-06-30
Redis的釋出與訂閱,有點類似於**訊息佇列**,傳送者往頻道傳送訊息,頻道的訂閱者接收訊息。
## 1. 釋出與訂閱示例
首先,在本機開啟第1個Redis客戶端,執行如下命令訂閱`blog.redis`頻道:
```shell
SUBSCRIBE "blog.redis"
```
![](https://images.zwwhnly.com/picture/2020/06/snipaste_20200609_203355.png)
然後,在本機開啟第2個Redis客戶端,執行相同的命令訂閱`blog.redis`頻道:
![](https://images.zwwhnly.com/picture/2020/06/snipaste_20200609_203606.png)
然後,開啟第3個Redis客戶端,執行如下命令往`blog.redis`頻道傳送訊息:
```shell
PUBLISH blog.redis "redis-in-action-01"
```
![](https://images.zwwhnly.com/picture/2020/06/snipaste_20200609_204046.png)
檢視客戶端1和客戶端2,分別看到如下資訊:
![](https://images.zwwhnly.com/picture/2020/06/snipaste_20200609_204512.png)
![](https://images.zwwhnly.com/picture/2020/06/snipaste_20200609_204554.png)
3個客戶端與頻道的關係如下圖所示:
![](https://images.zwwhnly.com/picture/2020/06/snipaste_20200623_195742.png)
可以通過`INFO clients`命令檢視連線的客戶端數:
![](https://images.zwwhnly.com/picture/2020/06/snipaste_20200610_094820.png)
## 2. 訂閱/退訂頻道
### 2.1 訂閱頻道
Redis的`SUBSCRIBE`命令用來訂閱頻道,使用方式如下所示:
```shell
SUBSCRIBE "blog.redis"
```
如果是訂閱多個頻道,可以使用如下所示命令:
```shell
SUBSCRIBE "blog.redis" "blog.rocketmq"
```
Redis將所有頻道的訂閱關係儲存在伺服器狀態的`pubsub_channels`字典裡,字典的鍵是某個被訂閱的頻道,鍵對應的值是1個連結串列,連結串列裡記錄了所有訂閱這個頻道的客戶端。
![](https://images.zwwhnly.com/picture/2020/06/snipaste_20200623_202331.png)
以上圖為例,說明客戶端1、客戶端2正在訂閱頻道"blog.redis",客戶端3、客戶端4正在訂閱頻道“blog.rocketmq”。
如果此時有1個客戶端5,執行了如下命令:
```shell
SUBSCRIBE "blog.rocketmq" "blog.java"
```
那麼伺服器狀態儲存的頻道訂閱關係將變為如下圖所示:
![](https://images.zwwhnly.com/picture/2020/06/snipaste_20200623_203337.png)
### 2.2 退訂頻道
Redis的`UNSUBSCRIBE`命令用來退訂頻道,使用方式如下所示:
```shell
UNSUBSCRIBE "blog.redis"
```
如果是退訂多個頻道,可以使用如下所示命令:
```shell
UNSUBSCRIBE "blog.redis" "blog.rocketmq"
```
假設現在伺服器狀態儲存的頻道訂閱關係如下圖所示:
![](https://images.zwwhnly.com/picture/2020/06/snipaste_20200623_203337.png)
如果此時客戶端5,執行了如下命令:
```shell
UNSUBSCRIBE "blog.rocketmq" "blog.java"
```
那麼伺服器狀態儲存的頻道訂閱關係將變為如下圖所示:
![](https://images.zwwhnly.com/picture/2020/06/snipaste_20200623_202331.png)
## 3. 訂閱/退訂模式
### 3.1 示例
首先,啟動1個Redis客戶端,執行如下命令訂閱模式“blog.r*”:
```shell
PSUBSCRIBE "blog.r*"
```
![](https://images.zwwhnly.com/picture/2020/06/snipaste_20200624_153651.png)
然後,啟動另1個Redis客戶端,執行`PUBLISH`命令往頻道傳送訊息:
```shell
PUBLISH "blog.redis" "redis-in-action-01"
PUBLISH "blog.rocketmq" "rocketmq-in-action-01"
PUBLISH "blog.java" "java-in-action-01"
```
![](https://images.zwwhnly.com/picture/2020/06/snipaste_20200624_154005.png)
可以看到,第1次啟動的客戶端可以接收到前2條訊息,因為頻道"blog.redis"、"blog.rocketmq"匹配模式“blog.r*”:
![](https://images.zwwhnly.com/picture/2020/06/snipaste_20200624_154147.png)
但頻道"blog.java"不匹配該模式,所以最後1次傳送的訊息,該客戶端未接收到。
### 3.2 訂閱模式
Redis的`PSUBSCRIBE`命令用來訂閱模式,使用方式如下所示:
```shell
PSUBSCRIBE "blog.r*"
```
如果是訂閱多個模式,可以使用如下所示命令:
```shell
PSUBSCRIBE "blog.r*" "blog.j?va" "blog.j[ae]va"
```
Redis將所有模式的訂閱關係儲存在伺服器狀態的`pubsub_patterns`屬性裡。
`pubsub_patterns`屬性是1個連結串列,連結串列中的每個節點是1個`pubsub_Pattern`結構,這個結構的`pattern`屬性記錄被訂閱的模式,`client`屬性記錄訂閱模式的客戶端。
![](https://images.zwwhnly.com/picture/2020/06/snipaste_20200629_103954.png)
以上圖為例,說明客戶端1正在訂閱模式"blog.r*,客戶端2正在訂閱模式“blog.j?va”。
如果此時有1個客戶端3,執行了如下命令:
```shell
PSUBSCRIBE "blog.j[ae]va"
```
那麼伺服器狀態儲存的模式訂閱關係將變為如下圖所示:
![](https://images.zwwhnly.com/picture/2020/06/snipaste_20200629_104411.png)
### 3.3 退訂模式
Redis的`PUNSUBSCRIBE`命令用來退訂模式,使用方式如下所示:
```shell
PUNSUBSCRIBE "blog.r*"
```
如果是退訂多個模式,可以使用如下所示命令:
```shell
PUNSUBSCRIBE "blog.j?va" "blog.j[ae]va"
```
假設現在伺服器狀態儲存的模式訂閱關係如下圖所示:
![](https://images.zwwhnly.com/picture/2020/06/snipaste_20200629_104411.png)
如果此時客戶端3,執行了如下命令:
```shell
PUNSUBSCRIBE "blog.j[ae]va"
```
那麼伺服器狀態儲存的模式訂閱關係將變為如下圖所示:
![](https://images.zwwhnly.com/picture/2020/06/snipaste_20200629_103954.png)
## 4. 傳送訊息
如果,伺服器狀態儲存的頻道訂閱關係如下圖所示:
![](https://images.zwwhnly.com/picture/2020/06/snipaste_20200623_202331.png)
伺服器狀態儲存的模式訂閱關係如下圖所示:
![](https://images.zwwhnly.com/picture/2020/06/snipaste_20200629_111658.png)
此時,如果1個Redis客戶端執行了以下PUBLISH命令:
```shell
PUBLISH blog.redis "redis-in-action-01"
```
那麼,伺服器會執行以下2個動作:
1. 將訊息"redis-in-action-01"傳送給頻道“blog.redis”的所有訂閱者
2. 將訊息"redis-in-action-01"傳送給與頻道“blog.redis”相匹配模式的訂閱者
也就是說,訊息"redis-in-action-01"不僅會發送給頻道“blog.redis”的訂閱者客戶端1、客戶端2,也會發送給與頻道“blog.redis”相匹配的模式“blog.r*”的訂閱者客戶端5。
## 5. 檢視訂閱資訊
可以使用Redis的`PUBSUB`命令來檢視頻道或者模式的相關資訊。
### 5.1 檢視被訂閱的頻道
如果想要檢視被訂閱的頻道資訊,可以使用命令`PUBSUB CHANNELS [pattern]`,其中pattern引數是可選的:
1. 如果不指定pattern引數,返回伺服器當前被訂閱的所有頻道
2. 如果指定pattern引數,返回伺服器被訂閱的頻道中與pattern模式相匹配的頻道
這個命令的實現原理是通過遍歷伺服器狀態儲存的`pubsub_channels`字典來實現的。
舉個具體的例子,如果伺服器狀態儲存的`pubsub_channels`字典如下所示:
![](https://images.zwwhnly.com/picture/2020/06/snipaste_20200623_203337.png)
那麼執行命令`PUBSUB CHANNELS`的返回結果如下所示:
![](https://images.zwwhnly.com/picture/2020/06/snipaste_20200629_115104.png)
執行命令`PUBSUB CHANNELS r*`的返回結果如下所示:
![](https://images.zwwhnly.com/picture/2020/06/snipaste_20200629_115236.png)
### 5.2 檢視頻道的訂閱者數量
如果想要檢視頻道的訂閱者數量,可以使用命令`PUBSUB NUMSUB [channel1 channel2 ... channeln]`。
這個命令的實現原理是通過遍歷伺服器狀態儲存的`pubsub_channels`字典來實現的,頻道對應的訂閱者連結串列的長度就是該頻道的訂閱者數量。
舉個具體的例子,如果伺服器狀態儲存的`pubsub_channels`字典如下所示:
![](https://images.zwwhnly.com/picture/2020/06/snipaste_20200623_203337.png)
那麼執行命令`PUBSUB NUMSUB blog.redis blog.rocketmq blog.java`的返回結果如下所示:
![](https://images.zwwhnly.com/picture/2020/06/snipaste_20200629_131543.png)
### 5.3 檢視被訂閱模式的數量
如果想要檢視被訂閱模式的數量,可以使用命令`PUBSUB NUMPAT`。
這個命令的實現原理是返回伺服器狀態儲存的`pubsub_patterns`連結串列的長度。
舉個具體的例子,如果伺服器狀態儲存的`pubsub_patterns`連結串列如下所示:
![](https://images.zwwhnly.com/picture/2020/06/snipaste_20200629_104411.png)
那麼執行命令`PUBSUB NUMPAT`的返回結果如下所示:
![](https://images.zwwhnly.com/picture/2020/06/snipaste_20200629_192026.png)
## 6. 總結
Redis的釋出與訂閱有點類似於訊息佇列的釋出與訂閱,主要包含以下7個命令:
1. SUBSCRIBE
2. UNSUBSCRIBE
3. PSUBSCRIBE
4. PUNSUBSCRIBE
5. PUBSUB CHANNELS
6. PUBSUB NUMSUB
7. PUBSUB NUMPAT
這7個命令的核心都是基於儲存在伺服器狀態的`pubsub_channels`字典和`pubsub_patterns`連結串列實現的。
## 7. 參考
黃健巨集 《Redis設計與實現》