MQTT協議
MQTT(Message Queuing Telemetry Transport Protocol):消息隊列遙感傳輸協議,是一種輕量級代理的發佈/訂閱模式的消息傳輸協議,運行在TCP協議棧之上,爲其提供有序、可靠、雙向連接的網絡連接保證。
之所以說是輕量級,是因爲Mqtt協議開銷非常小,協議頭只有2字節。
MQTT協議如何工作
MQTT是基於代理的發佈/訂閱消息傳輸模式。因此,在Mqtt中有三種角色:代理服務器(broker)、發佈者和訂閱者。其中發佈者和訂閱者互不干擾,通過代理服務器實現解耦,代理服務器會將來自發布者的消息進行存儲處理並將這些消息發送到正確的訂閱者中。
消息傳輸模型如下:
-
代理服務器
代理服務器可以是一個程序或者設備,作爲發送消息的客戶端和請求訂閱的客戶端之間的中介。其主要作用是接收發布者客戶端發佈的應用信息,然後將信息轉發給符合條件的訂閱者客戶端。 -
客戶端
客戶端指使用MQTT協議的程序或設備。客戶端包括髮布者客戶端和訂閱者客戶端,同一個客戶端可以即是發佈者也是訂閱者。客戶端可以發佈消息給其它相關客戶端,也可以訂閱其它客戶端發佈的消息。
如何將消息正確送達
MQTT通過“主題”實現將消息從發佈者客戶端送達至接收者客戶端。“主題”是附加在應用消息上的一個標籤,發佈者客戶端將“主題”和“消息”發送至代理服務器,代理服務器將該消息轉發至每一個訂閱了該“主題”的訂閱者客戶端。
主題Topic格式
一個主題可以由多個主題層級組成,每一層通過“/”斜槓分隔開。
主題過濾器指客戶端在訂閱時包含的一個表達式,用於表示相關的一個或多個主題。
如果用戶需要一次訂閱多個具有類似結構的主題,可以在主題過濾器中包含通配符。通配符只可用在主題過濾器中,在發佈應用消息時的主題名不允許包含通配符,主題通配符有兩種:
#:表示匹配>=0個層次,比如a/#就匹配a/b,a/b/c(不能匹配a/,後面必須有其它主題)。單獨的一個#表示匹配所有,不允許a#或a/#/c等形式。
+:表示匹配一個層次,例如a/+匹配a/b,a/c,不匹配a/b/c。單獨的一個+是允許的,但a+爲非法形式。
如何確保消息已被送達
Mqtt協議對發佈的消息提供了三種服務質量等級,通過設置PUBLISH報文中的QoS標誌位實現:
-
Qos=0:最多一次。此等級的消息不需要回應確認,也沒有重發機制,所以這類消息可能會丟失或者重複,取決於TCP/IP提供的盡最大努力交互的數據包服務。
-
Qos=1:最少一次。確保消息到達,但是消息可能發生重複,發送者如果在指定時間內沒有收到PUBACK控制報文,應用消息會被重新發送。
-
Qos=2:只有一次。最高級別的服務質量,消息丟失和重複都是不可接受的。(發送端有重發機制,在接收端會通過messageid判斷是不是重複,如果相等,則丟棄,確保不會重複)
臨終遺囑
MQTT協議利用KeepAlive機制在客戶端異常斷開時發現問題。當客戶端斷開時(例如:電量耗盡、系統崩潰或者網絡斷開),代理服務器會採取相應措施。
客戶端設置“臨終遺囑”(LWT)信息後,當代理服務器檢測到客戶端離線後,就會發送保存在特定主題上的 LWT 信息,讓其它訂閱該主題的客戶端知道該節點已經意外離線。
消息的緩存
如果客戶端在連接的時候指定Clean Session=false,那這個會話將成爲一個持久化會話。如果客戶端異常離線,broker將緩存“QoS=1”的消息,待客戶端重新連接後將消息發送至客戶端。需要注意以下情況:
-
消息只能緩存24小時。
-
客戶端重新連接時Clean Session需置爲False(flag=0)。
如果Clean Session爲True(flag=1),那麼當客戶端重新接入時,broker會丟棄已經緩存任何會話狀態信息,創建一個新的會話連接。
案例
- client_pub1向broker發送消息,client_sub1訂閱該topic,client_sub1可以正常收到client_pub1發出的消息。
- client_sub1異常離線,此時broker會緩存client_pub1發出的所有消息。
- 如果此時有其他訂閱相同主題的client_sub2接入到broker上,client_sub2是可以正常接收到client_pub1新發出來的消息,但是無法收到之前緩存的消息。
-
當client_pub1重新接入到broker後,根據狀態不同,有以下兩種處理方式:
-
如果clean session設爲false,那麼broker會將緩存的所有的消息發送給client_pub1。
-
如果clean session設爲true,那麼broker會丟棄所有緩存任何會話狀態信息,爲client_sub1重新創建一個新的連接。
-
持久化消息
如果PUBLISH消息固定頭部RETAIN標記爲1,broker會持久保存此消息,直到該消息被新的PUBLISH消息(RETAIN=1)覆蓋或用戶主動清除該消息。(即會持久化每個topic的最後一條消息)
對於“RETAIN=1”的消息,broker不但會將該消息發送給所有當前的訂閱者,同時新的接入設備也會收到該消息。
代碼示例
public class Test{
public static void main(String[] args) throws Exception {
String endpoint = "yourhostname"; //輸入創建endpoint返回的SSL地址
String username = "yourname"; //輸入創建thing返回的username
String password = "yourpwd"; //輸入創建principal返回的password
String topic = "topic1"; //訂閱的消息主題
MqttConnectOptions options = new MqttConnectOptions();
options.setCleanSession(true);
options.setUserName(username);
options.setPassword(password.toCharArray());
MqttClient client = new MqttClient(endpoint, "java-client"); //java-client爲標識設備的ID,用戶可自己定義,在同一個實例下,每個實體設備需要有一個唯一的ID
client.connect(options);
client.subscribe(topic);
MqttMessage message = new MqttMessage();
message.setPayload("15".getBytes());
client.publish(topic, message);
client.disconnect();
}
}