最通俗易懂的Redis釋出訂閱及程式碼實戰
阿新 • • 發佈:2020-04-26
### 釋出訂閱簡介
除了使用List實現簡單的訊息佇列功能以外,Redis還提供了釋出訂閱的訊息機制。在這種機制下,訊息釋出者向指定頻道(channel)釋出訊息,訊息訂閱者可以收到指定頻道的訊息,同一個頻道可以有多個訊息訂閱者,如下圖:
![在這裡插入圖片描述](https://img-blog.csdnimg.cn/20200328094449648.jpg#pic_center)
Redis也提供了一些命令支援這個機制,接下來我們詳細介紹一下這些命令。
歡迎關注微信公眾號:萬貓學社,每週一分享Java技術乾貨。
### 釋出訂閱相關命令
在Redis中,釋出訂閱相關命令有:
1. 釋出訊息
2. 訂閱頻道
3. 取消訂閱
4. 按照模式訂閱
5. 按照模式取消訂閱
6. 查詢訂閱資訊
#### 釋出訊息
釋出訊息的命令是`publish`,語法是:
```
publish 頻道名稱 訊息
```
比如,要向channel:one-more-study:demo頻道釋出一條訊息“I am One More Study.”,命令如下:
```
> publish channel:one-more-study:demo "I am One More Study."
(integer) 0
```
返回的結果是訂閱者的個數,上例中沒有訂閱者,所以返回結果為0。
#### 訂閱訊息
訂閱訊息的命令是`subscribe`,訂閱者可以訂閱一個或者多個頻道,語法是:
```
subscribe 頻道名稱 [頻道名稱 ...]
```
比如,訂閱一個channel:one-more-study:demo頻道,命令如下:
```
> subscribe channel:one-more-study:demo
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "channel:one-more-study:demo"
3) (integer) 1
```
返回結果中有3條,分別表示:返回值的型別(訂閱成功)、訂閱的頻道名稱、目前已訂閱的頻道數量。當訂閱者接受到訊息時,就會顯示:
```
1) "message"
2) "channel:one-more-study:demo"
3) "I am One More Study."
```
同樣也是3條結果,分別表示:返回值的型別(資訊)、訊息來源的頻道名稱、訊息內容。
新開啟的訂閱者,是無法收到該頻道之前的歷史訊息的,因為Redis沒有對釋出的訊息做持久化。
#### 取消訂閱
取消訂閱的命令是`unsubscribe`,可以取消一個或者多個頻道的訂閱,語法是:
```
unsubscribe [頻道名稱 [頻道名稱 ...]]
```
比如,取消訂閱channel:one-more-study:demo頻道,命令如下:
```
> unsubscribe channel:one-more-study:demo
1) "unsubscribe"
2) "channel:one-more-study:demo"
3) (integer) 0
```
返回結果中有3條,分別表示:返回值的型別(取消訂閱成功)、取消訂閱的頻道名稱、目前已訂閱的頻道數量。
歡迎關注微信公眾號:萬貓學社,每週一分享Java技術乾貨。
#### 按模式訂閱訊息
按模式訂閱訊息的命令是`psubscribe`,訂閱一個或多個符合給定模式的頻道,語法是:
```
psubscribe 模式 [模式 ...]
```
每個模式以 * 作為匹配符,比如 channel* 匹配所有以 channel 開頭的頻道,命令如下:
```
> psubscribe channel:*
Reading messages... (press Ctrl-C to quit)
1) "psubscribe"
2) "channel*"
3) (integer) 1
```
返回結果中有3條,分別表示:返回值的型別(按模式訂閱成功)、訂閱的模式、目前已訂閱的模式數量。當訂閱者接受到訊息時,就會顯示:
```
1) "pmessage"
2) "channel*"
3) "channel:one-more-study:demo"
4) "I am One More Study."
```
返回結果中有4條,分別表示:返回值的型別(資訊)、訊息匹配的模式、訊息來源的頻道名稱、訊息內容。
#### 按模式取消訂閱
按模式取消訂閱的命令是`punsubscribe`,可以取消一個或者多個模式的訂閱,語法是:
```
punsubscribe [模式 [模式 ...]]
```
每個模式以 * 作為匹配符,比如 channel:* 匹配所有以 channel 開頭的頻道,命令如下:
```
1> punsubscribe channel:*
1) "punsubscribe"
2) "channel:*"
3) (integer) 0
```
返回結果中有3條,分別表示:返回值的型別(按模式取消訂閱成功)、取消訂閱的模式、目前已訂閱的模式數量。
歡迎關注微信公眾號:萬貓學社,每週一分享Java技術乾貨。
#### 查詢訂閱資訊
##### 檢視活躍頻道
活躍頻道指的是至少有一個訂閱者的頻道,語法是:
```
pubsub channels [模式]
```
比如:
```
> pubsub channels
1) "channel:one-more-study:test"
2) "channel:one-more-study:demo"
3) "channel:demo"
> pubsub channels *demo
1) "channel:one-more-study:demo"
2) "channel:demo"
> pubsub channels *one-more-study*
1) "channel:one-more-study:test"
2) "channel:one-more-study:demo"
```
##### 檢視頻道訂閱數
```
pubsub numsub [頻道名稱 ...]
```
比如:
```
> pubsub numsub channel:one-more-study:demo
1) "channel:one-more-study:demo"
2) (integer) 1
```
##### 檢視模式訂閱數
```
> pubsub numpat
(integer) 1
```
### 程式碼實戰
光說不練假把式,我們使用Java語言寫一個簡單的釋出訂閱示例。
#### Jedis叢集示例
Jedis是Redis官方推薦的Java連線開發工具,我們使用Jedis寫一個簡單的叢集示例。
```java
package onemore.study;
import redis.clients.jedis.HostAndPort;
import redis.clients.jedis.JedisCluster;
import redis.clients.jedis.JedisPoolConfig;
import java.util.HashSet;
import java.util.Set;
/**
* Jedis叢集
*
* @author 萬貓學社
*/
public enum Cluster {
INSTANCE;
//為了簡單,把IP和埠直接寫在這裡,實際開發中寫在配置檔案會更好。
private final String hostAndPorts = "192.168.0.60:6379;192.168.0.61:6379;192.168.0.62:6379";
private JedisCluster jedisCluster;
Cluster() {
JedisPoolConfig poolConfig = new JedisPoolConfig();
//最大連線數
poolConfig.setMaxTotal(20);
//最大空閒數
poolConfig.setMaxIdle(10);
//最小空閒數
poolConfig.setMinIdle(2);
//從jedis連線池獲取連線時,校驗並返回可用的連線
poolConfig.setTestOnBorrow(true);
//把連線放回jedis連線池時,校驗並返回可用的連線
poolConfig.setTestOnReturn(true);
Set nodes = new HashSet<>();
String[] hosts = hostAndPorts.split(";");
for (String hostport : hosts) {
String[] ipport = hostport.split(":");
String ip = ipport[0];
int port = Integer.parseInt(ipport[1]);
nodes.add(new HostAndPort(ip, port));
}
jedisCluster = new JedisCluster(nodes, 1000, poolConfig);
}
public JedisCluster getJedisCluster() {
return jedisCluster;
}
}
```
歡迎關注微信公眾號:萬貓學社,每週一分享Java技術乾貨。
#### 釋出者示例
```java
package onemore.study;
import redis.clients.jedis.JedisCluster;
/**
* 釋出者
*
* @author 萬貓學社
*/
public class Publisher implements Runnable {
private final String CHANNEL_NAME = "channel:one-more-study:demo";
private final String QUIT_COMMAND = "quit";
@Override
public void run() {
JedisCluster jedisCluster = Cluster.INSTANCE.getJedisCluster();
for (int i = 1; i <= 3; i++) {
String message = "第" + i + "訊息";
System.out.println(Thread.currentThread().getName() + " 釋出:" + message);
jedisCluster.publish(CHANNEL_NAME, message);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("------------------");
}
jedisCluster.publish(CHANNEL_NAME, QUIT_COMMAND);
}
}
```
#### 訂閱者示例
```java
package onemore.study;
import redis.clients.jedis.JedisCluster;
import redis.clients.jedis.JedisPubSub;
/**
* 訂閱者
*
* @author 萬貓學社
*/
public class Subscriber implements Runnable {
private final String CHANNEL_NAME = "channel:one-more-study:demo";
private final String QUIT_COMMAND = "quit";
private final JedisPubSub jedisPubSub = new JedisPubSub() {
@Override
public void onMessage(String channel, String message) {
System.out.println(Thread.currentThread().getName() + " 接收:" + message);
if (QUIT_COMMAND.equals(message)) {
unsubscribe(CHANNEL_NAME);
}
}
};
@Override
public void run() {
JedisCluster jedisCluster = Cluster.INSTANCE.getJedisCluster();
jedisCluster.subscribe(jedisPubSub, CHANNEL_NAME);
}
}
```
歡迎關注微信公眾號:萬貓學社,每週一分享Java技術乾貨。
#### 綜合示例
```java
package onemore.study;
public class App {
public static void main(String[] args) throws InterruptedException {
//建立3個訂閱者
new Thread(new Subscriber()).start();
new Thread(new Subscriber()).start();
new Thread(new Subscriber()).start();
Thread.sleep(1000);
//建立釋出者
new Thread(new Publisher()).start();
}
}
```
執行結果如下:
```
Thread-6 釋出:第1訊息
Thread-0 接收:第1訊息
Thread-1 接收:第1訊息
Thread-2 接收:第1訊息
------------------
Thread-6 釋出:第2訊息
Thread-0 接收:第2訊息
Thread-1 接收:第2訊息
Thread-2 接收:第2訊息
------------------
Thread-6 釋出:第3訊息
Thread-0 接收:第3訊息
Thread-2 接收:第3訊息
Thread-1 接收:第3訊息
------------------
Thread-0 接收:quit
Thread-1 接收:quit
Thread-2 接收:quit
```
微信公眾號:萬貓學社
微信掃描二維碼
獲得更多Java技術乾貨