前言
在學習T-io框架,從寫一個Redis客戶端開始一文中,已經簡單介紹了Redis
客戶端的實現思路,並且基礎架構已經搭建完成,只不過支援的命令不全,不過後期在加命令就會很簡單了。本篇就要實現Publish/Subscribe
功能。
Pub/Sub
釋出訂閱模式在很多場景中用的都很頻繁,這裡不再贅述。下面看一下Redis
中的命令。參考資料:https://redis.io/topics/pubsub
//釋出
PUBLISH
//訂閱
SUBSCRIBE
//模式匹配訂閱
PSUBSCRIBE
//取消訂閱
UNSUBSCRIBE
//取消訂閱(模式匹配)
PUNSUBSCRIBE
//其他
PUBLISH/SUBSCRIBE
命令使用方式很簡單:
PUBLISH CHANNEL MESSAGE
例如:publish user helloworld
Client
類中增加程式碼:
@Override
public void publish(final String channel, final String message) {
sendCommand(Command.PUBLISH, channel, message);
}
@Override
public void subscribe(final String... channels) {
sendCommand(SUBSCRIBE, channels);
}
除錯程式碼如下:
//釋出
Tedis tedisPublish = new Tedis("192.168.1.225", 6379);
tedisPublish.publish("channel1","hello world");
//訂閱
Tedis tedis = new Tedis("192.168.1.225", 6379);
tedis.subscribe(new MyPubSub(),"channel1");
先訂閱,後釋出,訂閱響應結果:
*3
$9
subscribe
$8
channel1
:1
通過響應結果可以看出,我們當前的命令是 subscribe
,然後訂閱的是channel1
,當前共訂閱了:1
個。
釋出響應結果:
:1
總共發給了:1
個訂閱客戶端。這個結果就是訂閱客戶端的個數。
PSUBSCRIBE
命令格式如下:
PSUBSCIRBE news.*
修改一下除錯程式碼:
訂閱
tedis.pSubscribe(new MyPubSub(),"news.*");
響應結果:
*3
$10
psubscribe
$6
news.*
:1
釋出
tedisPublish.publish("news.sports","welcome to NBA");
tedisPublish.publish("news.country","this is china news");
訂閱客戶端收到訊息:
*4
$8
pmessage
$6
news.*
$11
news.sports
$14
welcome to NBA
*4
$8
pmessage
$6
news.*
$12
news.country
$18
this is china news
從響應結果可以看出,客戶端訂閱了 news.*
,然後收到了news.sports,news.country
的訊息。
響應訊息解析
上述程式碼中有一個MyPubSub
物件,它繼承自抽象類TedisPubSub
.這個類做了釋出訂閱核心的業務處理。通過對服務端返回的訊息格式,我們可以發現,它的訊息格式是統一的。
EVENT_NAME --事件
CHANNEL_NAME --頻道
OTHER --其他資訊,根據每個事件可能不同
所以我們在做釋出訂閱的響應訊息解析時,可以返回 List
private boolean handleSubscribe(byte[] resp,List<Object> reply){
//是否普通訂閱
boolean isSubscribe = Arrays.equals(SUBSCRIBE.raw, resp);
//是否模式匹配訂閱
boolean isPSubscribe = Arrays.equals(Keyword.PSUBSCRIBE.raw, resp);
if (isSubscribe || isPSubscribe) {
resetSubscribedChannels(reply);
//第二個值為 channel 名稱
final byte[] channelBytes = (byte[]) reply.get(1);
//轉化為 string
final String channel = getString(channelBytes);
//呼叫事件 (onSubscribe,onPSubscribe 子類可以重寫)
if (isSubscribe) {
onSubscribe(channel);
} else {
onPSubscribe(channel);
}
return true;
}
return false;
}
public abstract void onSubscribe(final String channel);
public abstract void onPSubscribe(final String channelPatterns);
在 MyPubSub
中重寫上述兩個方法。
@Override
public void onSubscribe(String channel) {
System.out.println("訂閱了:"+channel);
}
@Override
public void onPSubscribe(String channelPatterns) {
System.out.println("訂閱了:"+channelPatterns);
}
這樣,我們就能夠收到回撥訊息了。
訂閱了:news.*
接收到訊息同理:
@Override
public void onMessage(String channel, String message) {
System.out.println(channel + " 收到了訊息:"+message);
}
channel1 收到了訊息:welcome to NBA.
不過這裡需要注意的一點是,在普通訂閱的訊息中只有【MESSAGE,CHANNEL,CONTENT】三個值,而模式匹配的訂閱訊息中,有【PMESSAGE,PATTERN,CHANNEL,CONTENT】四個值,其中就多了一個 PATTERN
,也就是上文中的news.*
,所以稍微做一下區分就可以了
private boolean handleMessage(byte[] resp, List<Object> reply) {
boolean isMessage = Arrays.equals(MESSAGE.raw, resp);
boolean isPMessage = Arrays.equals(PMESSAGE.raw, resp);
if (isMessage || isPMessage) {
final byte[] secondBytes = (byte[]) reply.get(1);
final byte[] thirdBytes = (byte[]) reply.get(2);
final String second = getString(secondBytes);
final String third = getString(thirdBytes);
if (isMessage) {
onMessage(second, third);
} else {
final byte[] messageBytes = (byte[]) reply.get(3);
final String message = getString(messageBytes);
onPMessage(second, third, message);
}
return true;
}
呼叫示例
news.country(news.*)收到了訊息:this is china news
總結
本文簡單的對Redis
的Pub/Sub
模式做了介紹,並且在客戶端中做了相應的處理。當然其中也是大量參考了Jedis
原始碼。本文就到這裡啦,88
原始碼連結