未讀訊息(小紅點),前端與 RabbitMQ實時訊息推送實踐,賊簡單~
阿新 • • 發佈:2020-09-08
前幾天粉絲群裡有個小夥伴問過:`web` 頁面的未讀訊息(小紅點)怎麼實現比較簡單,剛好本週手頭有類似的開發任務,索性就整理出來供小夥伴們參考,沒準哪天就能用得上呢。
之前在 [《springboot + rabbitmq 做智慧家居》](https://mp.weixin.qq.com/s?__biz=MzAxNTM4NzAyNg==&mid=2247486353&idx=1&sn=02371acc8048cb15f29285f0505c4958&chksm=9b859b6cacf2127a2799d28830dc6ef0560764d4d29b5d3c742dd7489d66365691c662e70ce6&token=632378703&lang=zh_CN#rd) 中說過可以用 `rabbitmq` 的 `MQTT` 協議做智慧家居的指令推送,裡邊還提到過能用 `MQTT` 協議做 `web` 的訊息推送,而未讀訊息(`小紅點`)功能剛好應用到實時訊息推送了。
`MQTT` 協議就不再贅述了,沒接觸過的同學翻翻前邊的文章溫習一下吧,今天還是主要以實踐為主!
![](https://img-blog.csdnimg.cn/20200907183941327.png)
`web` 端實時訊息推送,常用的實現方式比較多,但萬變不離其宗,底層基本上還是依賴於 `websocket`,`MQTT` 協議也不例外。
## RabbitMQ 搭建
`RabbitMQ `的基礎搭建就不詳細說了,自行百度一步一步搞問題不大,這裡主要說一下兩個比較重要的配置。
### 1、開啟 mqtt 協議
預設情況下`RabbitMQ `是不開啟`MQTT` 協議的,所以需要我們手動的開啟相關的外掛,而`RabbitMQ `的`MQTT` 協議分為兩種。
第一種 `rabbitmq_mqtt` 提供與後端服務互動使用,對應埠`1883`。
```javascript
rabbitmq-plugins enable rabbitmq_mqtt
```
第二種 `rabbitmq_web_mqtt` 提供與前端互動使用,對應埠`15675`。
```javascript
rabbitmq-plugins enable rabbitmq_web_mqtt
```
在 `RabbitMQ` 管理後臺看到如下的顯示,就表示`MQTT` 協議開啟成功,到這中介軟體環境就搭建完畢了。
![協議對應埠號](https://img-blog.csdnimg.cn/20200908110952935.png)
使用`MQTT` 協議預設的交換機 `Exchange` 為 `amp.topic`,而我們訂閱的主題會在 `Queues` 註冊一個客戶端佇列,路由 `Routing key` 就是我們設定的主題。
![交換機資訊](https://img-blog.csdnimg.cn/20200908133812233.png)
## 服務端訊息傳送
`web` 端實時訊息推送一般都是單向的推送,前端接收服務端推送的訊息顯示即可,所以就只實現訊息傳送即可。
### 1、mqtt 客戶端依賴包
引入 `spring-integration-mqtt`、`org.eclipse.paho.client.mqttv3` 兩個工具包實現
```javascript
org.springframework.integration
spring-integration-mqtt
org.eclipse.paho
org.eclipse.paho.client.mqttv3
1.2.0
```
### 2、訊息傳送者
訊息的傳送比較簡單,主要是應用到 `@ServiceActivator` 註解,需要注意`messageHandler.setAsync`屬性,如果設定成 `false`,關閉非同步模式傳送訊息時可能會阻塞。
```javascript
@Configuration
public class IotMqttProducerConfig {
@Autowired
private MqttConfig mqttConfig;
@Bean
public MqttPahoClientFactory mqttClientFactory() {
DefaultMqttPahoClientFactory factory = new DefaultMqttPahoClientFactory();
factory.setServerURIs(mqttConfig.getServers());
return factory;
}
@Bean
public MessageChannel mqttOutboundChannel() {
return new DirectChannel();
}
@Bean
@ServiceActivator(inputChannel = "iotMqttInputChannel")
public MessageHandler mqttOutbound() {
MqttPahoMessageHandler messageHandler = new MqttPahoMessageHandler(mqttConfig.getServerClientId(), mqttClientFactory());
messageHandler.setAsync(false);
messageHandler.setDefaultTopic(mqttConfig.getDefaultTopic());
return messageHandler;
}
}
```
`MQTT` 對外提供傳送訊息的 `API` 時,需要使用 `@MessagingGateway` 註解,去提供一個訊息閘道器代理,引數 `defaultRequestChannel` 指定傳送訊息繫結的`channel`。
可以實現三種`API`介面,`payload` 為傳送的訊息,`topic` 傳送訊息的主題,`qos` 訊息質量。
```javascript
@MessagingGateway(defaultRequestChannel = "iotMqttInputChannel")
public interface IotMqttGateway {
// 向預設的 topic 傳送訊息
void sendMessage2Mqtt(String payload);
// 向指定的 topic 傳送訊息
void sendMessage2Mqtt(String payload,@Header(MqttHeaders.TOPIC) String topic);
// 向指定的 topic 傳送訊息,並指定服務質量引數
void sendMessage2Mqtt(@Header(MqttHeaders.TOPIC) String topic, @Header(MqttHeaders.QOS) int qos, String payload);
}
```
## 前端訊息訂閱
前端使用與服務端對應的工具 `paho-mqtt` `mqttws31.js`實現,實現方式與傳統的 `websocket` 方式差不多,核心方法 `client = new Paho.MQTT.Client` 和 各種監聽事件,程式碼比較簡潔。
**注意**:要保證前後端 `clientId`的全域性唯一性,我這裡就簡單用隨機數解決了
```javascript
```
## 測試
前後端的程式碼並不多,接下來我們測試一下,弄了個頁面看看效果。
首先用 `postman` 模擬後端傳送訊息
```javascript
http://127.0.0.1:8080/fun/sendMessage?message=我是程式設計師內點事&topic=push_message_topic
```
![模擬傳送訊息](https://img-blog.csdnimg.cn/20200908132348293.png)
再看一下前端訂閱訊息的效果,看到訊息被實時推送到了前端,這裡只做了未讀訊息數量統計,一般還會做未讀訊息詳情列表。
![實時訊息推送動圖](https://img-blog.csdnimg.cn/20200908132757419.gif#pic_center)
## 總結
未讀訊息是一個十分常見的功能,不管是 `web`端還是移動端系統都是必備的模組,`MQTT` 協議只是其中的一種實現方式,還是有必要掌握一種方法。具體用什麼工具實現還是要看具體的業務場景和學習成本,像我用`RabbitMQ` 做還考慮到一些運維成本在裡邊。
**本文完整程式碼地址**:`https://github.com/chengxy-nds/Springboot-Notebook/tree/master/springboot-mqtt-messagepush`
---
**原創不易,燃燒秀髮輸出內容,如果有一丟丟收穫,點個贊鼓勵一下吧!**
整理了幾百本各類技術電子書,送給小夥伴們。關注公號回覆【**666**】自行領取。和一些小夥伴們建了一個技術交流群,一起探討技術、分享技術資料,旨在共同學習進步,如果感興趣就加入我們吧!
![在這裡插入圖片描述](https://img-blog.csdnimg.cn/20200828110008952.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3hpbnpoaWZ1MQ==,size_16,color_FFFFFF,t_70#pic_center)