物聯網協議MQTT學習分享-介紹&安裝&java樣例 原 薦

背景

MQTT全稱:Message Queuing Telemetry Transport(消息隊列遙測傳輸協議),是一種基於發佈/訂閱(publish/subscribe)模式的"輕量、簡單、開放和易於實現的"通訊協議。

MQTT協議

官方定義:mqtt — MQ Telemetry Transport。官網:https://mosquitto.org/
它構建於TCP/IP協議上,由IBM在1999年發佈。MQTT最大優點在於,可以以極少的代碼和有限的帶寬,爲連接遠程設備提供實時可靠的消息服務。作爲一種低開銷、低帶寬佔用的即時通訊協議,使其在物聯網、小型設備、移動應用等方面有較廣泛的應用,如:機器與機器(M2M)通信和物聯網(IoT)。其在,通過衛星鏈路通信傳感器、偶爾撥號的醫療設備、智能家居中已廣泛使用。 不得不提:M2M和IoT,在這個領域裏面MQTT應用範圍很廣。

基礎通信架構(圖片來自網絡)

實現MQTT協議需要客戶端和服務器端通訊完成,在通訊過程中,MQTT協議中有三種身份:發佈者(Publish)、代理(Broker)(服務器)、訂閱者(Subscribe)。其中,消息的發佈者和訂閱者都是客戶端,消息代理是服務器,消息發佈者可以同時是訂閱者。
MQTT傳輸的消息分爲:主題(Topic)和負載(payload)兩部分:
(1)Topic,可以理解爲消息的類型,訂閱者訂閱(Subscribe)後,就會收到該主題的消息內容(payload)。
(2)payload,可以理解爲消息的內容,是指訂閱者具體要使用的內容。
MQTT會構建底層網絡傳輸:它將建立客戶端到服務器的連接,提供兩者之間的一個有序的、無損的、基於字節流的雙向傳輸。
當應用數據通過MQTT網絡發送時,MQTT會把與之相關的服務質量(QoS)和主題名(Topic)相關連。

Qos消息服務質量

Broker針對message提供的服務模式,不同模式對message的保證機制不同。

QoS=0:最多一次送達。
broker發送後就不管了,client不需要響應broker也不會重發。消息可能會丟失。

QoS=1:至少一次送達。
broker會維護msg生命週期。broker發出去之後必須等待反饋,沒有反饋就找時機重發,client反饋後broker結束該msg生命週期。可能會導致msg重複發情況。

QoS=2:確送達到且只有一次送達。最高等級消息服務質量模式,相應來說開銷會增大。
broker會維護msg生命週期。broker發送msg後等待client反饋,收到反饋後再發送給client確認收到反饋,client收到反饋確認後,再發送broker確認完成,broker結束msg生命週期。也就是兩次握手,第一次msg確認,第二次反饋確認。

Mosquitto中間件及其他Mqtt Broker

實現MQTT協議的中間件,市面上有很多,如RabbitMq就不同程度的實現了該協議。
圖片來自網絡

這裏我們主要介紹Mosquitto,它是eclipse產品下面。
Mosquitto is an open source implementation of a server for version 3.1 and 3.1.1 of the MQTT protocol. It also includes a C and C++ client library, and the mosquitto_pub and mosquitto_sub utilities for publishing and subscribing.
它包含C&C++的庫,主要是mosquitto.h這個庫,封裝了很多方法。可以參考文檔:https://mosquitto.org/api/files/mosquitto-h.html

對於JAVA下的用戶推薦 org.eclipse.paho\rg.eclipse.paho.client.mqttv3 工具包,目前大多數公司都使用這款。因爲MQTT基於TCP/IP且原理簡單,也可以自研connect-client或者站在開源的基礎上封裝。

安裝Mosquitto(window|linux)

下載地址:https://mosquitto.org/download/

1、window安裝方式
直接下載exe文件,進行安裝即可。
如果window盜版,大致會出現dll包丟失問題(反正我是出現了),按要求去網上下載複製到c:\windows\system32目錄下。

安裝後目錄如下:
目錄

需要掌握以下這幾個文件:

  • mosquitto.conf: 這是配置文件,可以修改端口、消息內容大小等等。
  • mosquitto.exe:這是中間件服務(broker),首先就係統啓動它才能提供MQTT協議的消息服務。
  • mosquitto_sub.exe:這是訂閱消息命令工具。
  • mosquitto_pub.exe:這是發佈消息命令工具。
  • mosquitto_passwd.exe:用來管理密碼。

具體命令詳情,參考官網文檔:
mosquitto_conf
mosquitto
mosquitto_sub
mosquitto_passwd
mosquitto_pub

2、linux安裝方式
官網直接下載 mosquitto-1.5.4.tar.gz
我的linux環境:2.6.32-696.el6.x86_64
tar -xvf mosquitto-1.5.4.tar.gz,會生成目錄:mosquitto-1.5.4
進入該目錄執行: make install
不同環境會遇到不同的坑:我遇到的是少了文件:uuid/uuid.h,如下圖:

百度查了下,需要安裝 libuuid-devel ,使用 yum install libuuid-devel,之後在 make install 順利成功。
成功後會生成命令:mosquitto mosquitto_passwd mosquitto_pub mosquitto_sub
配置文件生成在:/etc/mosquitto 目錄下
啓動mosquitto命令:mosquitto -c /etc/mosquitto/mosquitto.conf -d
又遇到個坑:libmosquitto.so.1 路徑不正確。
編譯完mosquitto之後,進入到lib目錄下,將編譯之後的libmosquitto.so.1 拷貝到目錄/usr/local/lib下,執行如下命令:
cp libmosquitto.so.1 /usr/local/lib
然後再執行: ln -s /usr/local/lib/libmosquitto.so.1 /usr/lib/libmosquitto.so.1 和 ldconfig 即可。

測試是否成功三個命令:

  • mosquitto -c /etc/mosquitto/mosquitto.conf -d
  • mosquitto_sub -h 127.0.0.1 -p 1885 -t test001
  • mosquitto_pub -h 127.0.0.1 -p 1885 -t test001 -m "hello"

使用mosquitto,及mosquitto、mosquitto_sub、mosquitto_pub命令

mosquitto 就是中間件服務器,類似我們使用的rocketmq、redis,安裝後需要啓動這個服務。 mosquitto_sub\mosquitto_pub 這兩個是訂閱\發佈命令。

官方提供詳細文檔介紹命令如何使用,如下:

  • mosquitto -c ./mosquitto.conf -p 1883 -v
  • mosquitto_sub -h 127.0.0.1 -p 1883 -t Test/test/1
  • mosquitto_pub -h 192.168.1.1 -p 1885 -t sensors/temperature -m "1266193804 32"

window安裝的話會有mosquitto_sub.exe\mosquitto_pub.exe可執行文件,你可以直接CMD使用。如下圖: 發佈topic
訂閱topic

項目中如何集成

JAVA項目推薦mqttv3包,eclipse旗下官方包。

<dependency>
    <groupId>org.eclipse.paho</groupId>
    <artifactId>org.eclipse.paho.client.mqttv3</artifactId>
    <version>1.2.0</version>
</dependency>

mqttv3設計原理詳解

eclipse-paho mqttv3模型結構:

mqttv3定義了兩個接口實現分別是:MqttClient和MqttAsyncClient。

消息持久化兩種模式:MemoryPersistence和MqttDefaultFilePersistence。

mqttv3並沒有實現訂閱的保持策略,需要自己去維護。

樣例代碼展示

1、發佈消息

@Test
public void mqttPub(){
    try {
        MqttConnectOptions options = new MqttConnectOptions();
        options.setKeepAliveInterval(20);
        options.setConnectionTimeout(10);
        options.setCleanSession(false);

        mqttClient = new MqttClient(serverUri, clientIp, new MemoryPersistence());
        mqttClient.setCallback(new MqttCallBackImpl(mqttClient));
        IMqttToken mqttToken = mqttClient.connectWithResult(options);
        boolean isComplete = mqttToken.isComplete();
        System.out.println("connect is " + isComplete);

        int count = 50;
        while (count-->0) {
            String msg = "hello world : " + System.currentTimeMillis();
            MqttMessage mqttMessage = new MqttMessage();
            mqttMessage.setQos(2);
            mqttMessage.setPayload(msg.getBytes("UTF-8"));
            mqttClient.publish(testTopic, mqttMessage);
            Thread.sleep(500);
        }
        System.out.println("mqttPub is closed.");
		closeMqtt();
    } catch (UnsupportedEncodingException e){
        e.printStackTrace();
    } catch (MqttException e) {
        e.printStackTrace();
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}

2、訂閱消息callback方式

@Test
public void mqttSubByCallBack(){
    try {
        MqttConnectOptions options = new MqttConnectOptions();
        options.setKeepAliveInterval(20);
        options.setConnectionTimeout(10);
        options.setCleanSession(false);

        mqttClient = new MqttClient(serverUri, clientIp, new MemoryPersistence());
        mqttClient.setCallback(new MqttCallBackImpl());
        IMqttToken mqttToken = mqttClient.connectWithResult(options);
        boolean isComplete = mqttToken.isComplete();
        System.out.println("connect is " + isComplete);

        int count = 50;
        while (count-- > 0) {
            mqttClient.subscribe(testTopic);
            Thread.sleep(300);
        }
        System.out.println("mqttSubByCallBack is closed.");
		closeMqtt();
    } catch (MqttException e) {
        e.printStackTrace();
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}

3、訂閱消息messagelistener方式

@Test
public void mqttSubByListener(){
    try {
        MqttConnectOptions options = new MqttConnectOptions();
        options.setKeepAliveInterval(20);
        options.setConnectionTimeout(10);
        options.setCleanSession(false);

        mqttClient = new MqttClient(serverUri, clientIp, new MemoryPersistence());
        IMqttToken mqttToken = mqttClient.connectWithResult(options);
        boolean isComplete = mqttToken.isComplete();
        System.out.println("connect is " + isComplete);

        int count = 50;
        while(count-- > 0){
            mqttClient.subscribeWithResponse(testTopic, new MqttMessageListenerImpl());
            Thread.sleep(200);
        }
        System.out.println("mqttSubByListener is closed.");
		closeMqtt();
    } catch (MqttException e) {
        e.printStackTrace();
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}

作者:Owen Jia , 歡迎關注他的博客 Owen Blog

掃描下方二維碼,收藏本篇博客哦。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章