【MQTT系列】Eclipse Mosquitto實戰

工匠小豬豬的技術世界發表於2019-01-11

雖然我強烈推薦使用EMQ,但是Mosquitto也是非常有必要了解的。

Mosquitto的英文意思是 蚊子 。

由於Mosquitto的安裝比較簡單,本文不會具體描述,本文測試介紹Mosquitto的概念、客戶端以及如何使用Java程式碼跑通Mosquitto的功能。
在文章的最後,也會說明Mosquitto存在的一些問題以及我不選擇它的一些理由。

Mosquitto介紹

Eclipse Mosquitto是一個開源(EPL / EDL許可)訊息代理,它實現了MQTT協議版本3.1和3.1.1。Mosquitto重量輕,適用於從低功耗單板計算機到完整伺服器的所有裝置。

MQTT協議提供了一種使用釋出/訂閱模型執行訊息傳遞的輕量級方法。這使其適用於物聯網訊息傳遞,例如低功率感測器或移動裝置,如電話,嵌入式計算機或微控制器。

Mosquitto專案還提供了用於實現MQTT客戶端的C庫,以及非常流行的mosquitto_pub和mosquitto_sub命令列MQTT客戶端。

Mosquitto是Eclipse Foundation的一部分,是一個iot.eclipse.org專案。

官網: 

【MQTT系列】Eclipse Mosquitto實戰


【MQTT系列】Eclipse Mosquitto實戰

【MQTT系列】Eclipse Mosquitto實戰

【MQTT系列】Eclipse Mosquitto實戰


正常來說,如果是Java專案我們可以引入如下依賴

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

Mosquitto Java 客戶端實現

Mosquitto 訊息傳送主要分為三個類:

  • PubMsg 客服端釋出訊息

  • PushCallback 訊息回撥

  • SubMsg 訂閱訊息

下面我們一起來看這個示例程式:

PubMsg

package com.example.mqtt.publish;

import org.eclipse.paho.client.mqttv3.*;
import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence;

/**
 * 傳送訊息到MQTT
 */
public class PubMsg {
    private static int qos = 2; //只有一次
    private static String broker = "tcp://XXXXXXXXX:1883";
    private static String userName = "admin";
    private static String passWord = "admin";


    private static MqttClient connect(String clientId, String userName,
                                      String password) throws MqttException {
        MemoryPersistence persistence = new MemoryPersistence();
        MqttConnectOptions connOpts = new MqttConnectOptions();
        connOpts.setCleanSession(true);
        connOpts.setUserName(userName);
        connOpts.setPassword(password.toCharArray());
        connOpts.setConnectionTimeout(10);// 設定超時時間
        connOpts.setKeepAliveInterval(20); // 設定會話心跳時間
//      String[] uris = {"tcp://10.100.124.206:1883","tcp://10.100.124.207:1883"};
//      connOpts.setServerURIs(uris);  //起到負載均衡和高可用的作用

        // broker,clientid即連線MQTT的客戶端ID,一般以唯一識別符號表示,MemoryPersistence設定clientid的儲存形式,預設為以記憶體儲存
        MqttClient mqttClient = new MqttClient(broker, clientId, persistence);


/*        // MQTT的連線設定
        MqttConnectOptions options = new MqttConnectOptions();
        // 設定是否清空session,這裡如果設定為false表示伺服器會保留客戶端的連線記錄,這裡設定為true表示每次連線到伺服器都以新的身份連線
        options.setCleanSession(true);
        // 設定連線的使用者名稱
        options.setUserName(userName);
        // 設定連線的密碼
        options.setPassword(passWord.toCharArray());
        // 設定超時時間 單位為秒
        options.setConnectionTimeout(10);
        // 設定會話心跳時間 單位為秒 伺服器會每隔1.5*20秒的時間向客戶端傳送個訊息判斷客戶端是否線上,但這個方法並沒有重連的機制
        options.setKeepAliveInterval(20);
        MqttTopic topic = mqttClient.getTopic("test-topic");
        // setWill方法,如果專案中需要知道客戶端是否掉線可以呼叫該方法。設定最終埠的通知訊息
        options.setWill(topic, "close".getBytes(), 2, true);*/

        mqttClient.setCallback(new PushCallback("test"));
        mqttClient.connect(connOpts);
        return mqttClient;
    }

    private static void pub(MqttClient sampleClient, String msg, String topic)
            throws MqttPersistenceException, MqttException {
        MqttMessage message = new MqttMessage("Hello Charles!".getBytes());
        message.setQos(qos);
        message.setRetained(false);
        sampleClient.publish(topic, message);
    }

    private static void publish(String str, String clientId, String topic) throws MqttException {
        MqttClient mqttClient = connect(clientId, userName, passWord);

        if (mqttClient != null) {
            pub(mqttClient, str, topic);
            System.out.println("pub-->" + str);
        }

        if (mqttClient != null) {
            mqttClient.disconnect();
        }
    }

    public static void main(String[] args) throws MqttException {
        publish("message content", "client-id-0", "test-topic");
    }
}

PushCallback

package com.example.mqtt.publish;

import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken;
import org.eclipse.paho.client.mqttv3.MqttCallback;
import org.eclipse.paho.client.mqttv3.MqttMessage;


/**
 * 釋出訊息的回撥類
 *
 * 必須實現MqttCallback的介面並實現對應的相關介面方法CallBack 類將實現 MqttCallBack。
 * 每個客戶機標識都需要一個回撥例項。在此示例中,建構函式傳遞客戶機標識以另存為例項資料。 在回撥中,將它用來標識已經啟動了該回撥的哪個例項。
 * 必須在回撥類中實現三個方法:
 *
 * public void messageArrived(MqttTopic topic, MqttMessage message)接收已經預訂的釋出。
 *
 * public void connectionLost(Throwable cause)在斷開連線時呼叫。
 *
 * public void deliveryComplete(MqttDeliveryToken token)) 接收到已經發布的 QoS 1 或 QoS 2
 * 訊息的傳遞令牌時呼叫。 由 MqttClient.connect 啟用此回撥。
 *
 */
class PushCallback implements MqttCallback {
    private String threadId;

    public PushCallback(String threadId) {
        this.threadId = threadId;
    }

    public void connectionLost(Throwable cause) {
        // 連線丟失後,一般在這裡面進行重連
        System.out.println("連線斷開,可以做重連");
    }

    public void deliveryComplete(IMqttDeliveryToken token) {
       System.out.println("deliveryComplete---------" + token.isComplete());
    }

    public void messageArrived(String topic, MqttMessage message) throws Exception {
        String msg = new String(message.getPayload());
        System.out.println("-------messageArrived-------"+threadId + " " + msg);
    }
}

SubMsg

package com.example.mqtt.subscribe;

import org.eclipse.paho.client.mqttv3.IMqttMessageListener;
import org.eclipse.paho.client.mqttv3.MqttClient;
import org.eclipse.paho.client.mqttv3.MqttConnectOptions;
import org.eclipse.paho.client.mqttv3.MqttException;
import org.eclipse.paho.client.mqttv3.MqttMessage;
import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence;

public class SubMsg {


    private static int qos = 2;
    private static String broker = "tcp://XXXXXXX:1883";


    private static MqttClient connect(String clientId) throws MqttException{
        MemoryPersistence persistence = new MemoryPersistence();
        MqttConnectOptions connOpts = new MqttConnectOptions();

        connOpts.setCleanSession(false);

        connOpts.setConnectionTimeout(10);
        connOpts.setKeepAliveInterval(20);
        connOpts.setUserName("admin");
        connOpts.setPassword("admin".toCharArray());

        MqttClient mqttClient = new MqttClient(broker, clientId, persistence);
        mqttClient.connect(connOpts);
        return mqttClient;
    }

    public static void sub(MqttClient mqttClient,String topic) throws MqttException{
        int[] Qos  = {qos};
        String[] topics = {topic};
        mqttClient.subscribe(topics, Qos);
    }


    private static void runsub(String clientId, String topic) throws MqttException{
        MqttClient mqttClient = connect(clientId);
        if(mqttClient != null){
            sub(mqttClient,topic);
        }
        mqttClient.subscribe(topic,2, new IMqttMessageListener() {

            @Override
            public void messageArrived(String topic, MqttMessage message) throws Exception {
                System.out.println("接收訊息主題 : " + topic);
                System.out.println("接收訊息Qos : " + message.getQos());
                System.out.println("接收訊息內容 : " + new String(message.getPayload()));
            }
        });
    }
    public static void main(String[] args) throws MqttException{

        runsub("testSub", "test-topic");
    }
}

執行結果

啟動SubMsg訂閱訊息,再啟動PubMsg傳送訊息

1)PubMsg顯示傳送成功:

【MQTT系列】Eclipse Mosquitto實戰

2)SubMsg也會收到資訊:

【MQTT系列】Eclipse Mosquitto實戰

為什麼線上環境不考慮使用Mosquitto?

線上環境需要使用叢集模式,雖然官方可以使用bridge功能,連線多個mqtt broker,但是還是存在一些問題。原因主要有如下幾個:

  • Mosquitto半年多沒維護了,版本也不是最新的

  • 如果增加bridge節點,需要修改多個節點的配置,並且重啟,維護成本高

  • 叢集模式如果主節點down機,所有從伺服器會變成孤立的節點

  • down機節點重新啟動以後,容易造成負載不均衡

  • 我們有更好的方案EMQ

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/31555484/viewspace-2375531/,如需轉載,請註明出處,否則將追究法律責任。

相關文章