Java基於ActiveMq 客戶端的MQTT實現
MQTT(Message Queuing Telemetry Transport,消息隊列遙測傳輸協議),是一種基於發佈/訂閱(publish/subscribe)模式的“輕量級”通訊協議,MQTT消息的發送和訂閱都是依賴MQTT服務器的,沒有MQTT服務器,你的客戶端是無法訂閱和發送消息的。所以在最開始的時候,可以選擇性的在你的電腦上面安裝一個MQTT服務器。
這裏需要了解一些概念:
(1)MQTT協議實現方式
實現MQTT協議需要客戶端和服務器端通訊完成,在通訊過程中,MQTT協議中有三種身份:發佈者、代理、訂閱者,其中消息的發佈者和訂閱者都是客戶端,消息代理是服務器,消息發佈者也可以是訂閱者。
MQTT傳輸的消息分爲:主題(Topic)和負載(payload)兩部分:
主題:可以理解爲消息的類型,訂閱者訂閱(Subscribe)後,就會收到該主題的消息內容(payload)
負載:可以理解爲消息內容,是指的訂閱者具體收到的數據。
(2)MQTT客戶端
發佈消息給其它相關的客戶端。
訂閱以請求接受相關的消息。
取消訂閱以移除接受消息的請求。
從服務端斷開連接
(3)服務器
接受來自客戶端的網絡連接。
接受客戶端發佈的應用消息。
處理客戶端的訂閱和取消訂閱請求。
轉發應用消息給符合條件的已訂閱客戶端。
MQ是消息中間件,是一種在分佈式系統中應用程序藉以傳遞消息的媒介,常用的有ActiveMQ,RabbitMQ,kafka。ActiveMQ是Apache下的開源項目,完全支持JMS1.1和J2EE1.4規範的JMS Provider實現。 (我們這裏使用的ActiveMQ)
1 安裝apache-activemq
(1) http://activemq.apache.org 進行下載需要的版本,這裏是安裝在Windows上,下載好的壓縮包,進行解壓。
(2)進入到指定目錄,運行/bin/win32或win64 activemq.bat
(3)這樣我們的ActiveMq 就運行,然後在瀏覽器中輸入http://localhost:8161 剛開始需要登錄,初始賬號h和密碼:admin ,這樣服務器就部署好了。
2 業務需求
具體的實現方式,根據業務不同,代碼邏輯不同,我這裏定義了設備信息採集的客戶端,和接收所有設備信息的服務端,
(1)客戶端設備信息能實時上傳
(2)客戶端可以實時獲取最新的設備參數等數據
(3)服務器能實時更新設備參數和加載最新數據
(4)其他需求
3 具體實現
(1)瞭解Paho
Paho Java客戶端是一個用Java編寫的MQTT客戶端庫,用於開發在JVM或其他Java兼容平臺(如Android)上運行的應用程序。
Paho Java客戶端提供了兩個API:MqttAsyncClient提供了一個完全異步的API,通過已註冊的回調通知完成活動。 MqttClient是MqttAsyncClient的一個同步包裝,其中函數與應用程序同步。
(2)添加依賴
<dependency>
<groupId>org.eclipse.paho</groupId>
<artifactId>org.eclipse.paho.client.mqttv3</artifactId>
<version>1.2.0</version>
</dependency>
不管是客戶端還是服務端都要加,對於paho不存在客戶端和服務端,消息的訂閱與發送都是可以,只是通過代碼的業務邏輯實現,有些消息只有客戶端能訂閱和發送,有些消息只有服務端能訂閱和發送。
(3)連接 MqttClient
在程序加載的時候啓動線程,連接mqtt服務器,完成一些數據初始化操作
1 連接mqtt
private MqttClient client;
private JedisClient jedisClient;
private String scanSrc;
private IWorkshopAlarmDao alarmDao;
private String mqttHost;
private String clientId;
private String userName;
private String passWord;
private MqttConnectOptions options;
private int qos = 2;
public void run(){
try {
System.out.println("連接MQTT服務。。。。。");
connectMqtt();
startServer();
System.out.println("MQTT 訂閱服務。。。。。");
}catch (Exception e){
e.printStackTrace();
}
}
public void connectMqtt(){
try {
// client = new MqttAsyncClient(mqttHost, clientId, new MemoryPersistence());
client = new MqttClient(mqttHost, clientId, new MemoryPersistence());
options = new MqttConnectOptions();
options.setCleanSession(true);
options.setUserName(userName);
options.setPassword(passWord.toCharArray());
// 設置超時時間
options.setConnectionTimeout(10);
// 設置會話心跳時間
options.setKeepAliveInterval(90);
client.setCallback(new PublishMessage());
} catch (Exception e) {
e.printStackTrace();
}
}
2 設置斷開重連
public void startServer() {
try {
while (true) {
try {
//判斷攔截狀態,這裏注意一下,如果沒有這個判斷,是非常坑的
if (!client.isConnected()) {
System.out.println("*****嘗試連接mqtt *****");
client.connect(options);
}
if (client.isConnected()) {//連接成功,跳出連接
System.out.println("*****已連接mqtt success *****");
break;
}
sleep(2000);
} catch (MqttException e1) {
System.out.println("連接MQTT服務失敗。。。。。。。。。。。。");
}
}
//訂閱消息
subTopic();
initMessage();
} catch (Exception e) {
e.printStackTrace();
}
}
3 在初始化的時候需要訂閱消息topic
public void subTopic(){
//訂閱消息
int[] Qos = {qos};
// String[] reg_workshop = {TopicConst.reg_workshop};
// String[] reg_aiservice = {TopicConst.reg_aiservice};
// String[] reg_camera = {TopicConst.reg_camera};
// String[] reg_gate = {TopicConst.reg_gate};
String[] init_reg = {TopicConst.init_reg};
String[] get_worker = {TopicConst.get_worker};
String[] update_workshop = {TopicConst.update_workshop};
String[] update_aiservice = {TopicConst.update_aiservice};
String[] update_camera = {TopicConst.update_camera};
String[] update_gate = {TopicConst.update_gate};
String[] workshop_record = {TopicConst.workshop_record};
String[] delete_gate = {TopicConst.delete_gate};
String[] delete_camera = {TopicConst.delete_camera};
String[] workshop_reset = {TopicConst.workshop_reset};
try {
// client.subscribe(reg_workshop, Qos);
// client.subscribe(reg_aiservice, Qos);
// client.subscribe(reg_camera, Qos);
// client.subscribe(reg_gate, Qos);
client.subscribe(get_worker, Qos);
client.subscribe(update_workshop, Qos);
client.subscribe(update_aiservice, Qos);
client.subscribe(update_camera, Qos);
client.subscribe(update_gate, Qos);
client.subscribe(workshop_record, Qos);
client.subscribe(delete_gate, Qos);
client.subscribe(delete_camera, Qos);
client.subscribe(init_reg, Qos);
client.subscribe(workshop_reset, Qos);
} catch (MqttException e) {
e.printStackTrace();
}
}
4 消息接收,業務邏輯處理
class PublishMessage implements MqttCallback {
public void connectionLost(Throwable cause) {
// 連接丟失後,一般在這裏面進行重連
System.out.println("連接斷開,可以做重連");
// connectMqtt();
StopService();
startServer();
}
public void deliveryComplete(IMqttDeliveryToken token) {
System.out.println("deliveryComplete---------" + token.isComplete());
}
public void messageArrived(String topic, MqttMessage message) {
// subscribe後得到的消息會執行到這裏面
byte[] messByte = message.getPayload();
System.out.println("接收消息主題 : " + topic);
System.out.println("接收消息Qos : " + message.getQos());
try {
// System.out.println("接收消息內容 : " + new String(messByte).getBytes("utf8"));
} catch (Exception e) {
e.printStackTrace();
}
if (topic == null) {
return;
}
try {
String data = new String(messByte, "utf-8");
if (topic.equals(TopicConst.init_reg)) {
initReg(data);
} else if (topic.equals(TopicConst.update_workshop)) {
updateWorkShop(data);
} else if (topic.equals(TopicConst.update_aiservice)) {
updateAiservier(data);
} else if (topic.equals(TopicConst.update_camera)) {
updateCamera(data);
} else if (topic.equals(TopicConst.update_gate)) {
updateGate(data);
} else if (topic.equals(TopicConst.get_worker)) {
if (data.equals("1")) {
sendWorker();
}
} else if (topic.equals(TopicConst.workshop_record)) {
reportRecords(data);
} else if (topic.equals(TopicConst.delete_gate)) {
deleteGate(data);
} else if (topic.equals(TopicConst.delete_camera)) {
deleteCamera(data);
}else if (topic.equals(TopicConst.workshop_reset)) {
resetWorkShop(data);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
5 發送消息
public void publish(MqttTopic topic , MqttMessage message) throws MqttException {
MqttDeliveryToken token = topic.publish(message);
token.waitForCompletion();
System.out.println("message is published completely! " + token.isComplete());
}
6 服務斷開做相關處理
public void StopService() {
try {
if(client !=null){
// 斷開連接
client.disconnect();
// 關閉客戶端
client.close();
}
} catch (MqttException e) {
e.printStackTrace();
}
}
這裏沒有做相關Mqtt 服務,而是把消息訂閱與分佈和具體的業務代碼融合。
使用org.eclipse.paho 工具進行消息的訂閱和發送
工具的使用很簡單,不說明。