MQTT協議及其使用案例

来源:https://www.cnblogs.com/xietingwei/p/18026662
-Advertisement-
Play Games

MQTT 概述 MQTT是基於TCP/IP協議棧構建的非同步通信消息協議,是一種輕量級的發佈、訂閱信息傳輸協議。 可以在不可靠的網路環境中進行擴展,適用於設備硬體存儲空間或網路帶寬有限的場景。 使用MQTT協議,消息發送者與接收者不受時間和空間的限制。 Docker 部署 MQTT(採用docker- ...


MQTT 概述

MQTT是基於TCP/IP協議棧構建的非同步通信消息協議,是一種輕量級的發佈、訂閱信息傳輸協議。 可以在不可靠的網路環境中進行擴展,適用於設備硬體存儲空間或網路帶寬有限的場景。 使用MQTT協議,消息發送者與接收者不受時間和空間的限制。

Docker 部署 MQTT(採用docker-compose.yml)

version: "3" 
services:
    mqtt:
        image: eclipse-mosquitto
        container_name: mqtt
        privileged: true 
        ports: 
            - 1883:1883
            - 9001:9001
        volumes:
            - ./config:/mosquitto/config
            - ./data:/mosquitto/data
            - ./log:/mosquitto/log
  • 文件夾
    image

  • 創建 config/mosquitto.conf

persistence true
listener 1883
persistence_location /mosquitto/data
log_dest file /mosquitto/log/mosquitto.log
 
# 關閉匿名模式
# allow_anonymous true
# 指定密碼文件
password_file /mosquitto/config/pwfile.conf
  • docker部署執行:docker compose up -d
  • 設置訪問許可權(用戶名:admin,密碼:admin123)
docker exec -it mqtt sh
touch /mosquitto/config/pwfile.conf
chmod -R 755 /mosquitto/config/pwfile.conf
mosquitto_passwd -b /mosquitto/config/pwfile.conf admin admin123
  • 重啟mqtt容器:docker compose restart

Springboot 整合

  • 依賴
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.5.5</version>
        <relativePath/>
    </parent>
    
    <dependencies>
    		<!--  spring mqtt協議  -->
        <dependency>
            <groupId>org.springframework.integration</groupId>
            <artifactId>spring-integration-mqtt</artifactId>
        </dependency>
        <!--  lombok  -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
        <!--spring boot and web-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.62</version>
        </dependency>

        <!--Http 請求 組件-->
        <dependency>
            <groupId>org.apache.httpcomponents</groupId>
            <artifactId>httpclient</artifactId>
        </dependency>
        <!--測試組件-->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-test</artifactId>
        </dependency>
    </dependencies>
  • 配置文件
mqtt.host=tcp://127.0.0.1:1883
mqtt.clientId=mqttx_a071ba88
mqtt.username=admin
mqtt.password=admin123
mqtt.topic=test_topic
mqtt.timeout=36000
mqtt.keepAlive=6000
  • 配置類
@Slf4j
@Configuration
public class MyMqttConfiguration {
    @Value("${mqtt.host}")
    String broker;
    @Value("${mqtt.clientId}")
    String clientId;
    @Value("${mqtt.username}")
    String username;
    @Value("${mqtt.password}")
    String password;
    @Value("${mqtt.timeout}")
    Integer timeout;
    @Value("${mqtt.keepAlive}")
    Integer keepAlive;
    @Value("${mqtt.topic}")
    String topic;
    @Autowired
    MyHandle myHandle;

    @Bean
    public MyMqttClient myMqttClient(){
        MyMqttClient mqttClient = new MyMqttClient(broker, username, password, clientId, timeout, keepAlive,myHandle);
        for (int i = 0; i < 10; i++) {
            try {
                mqttClient.connect();
                mqttClient.subscribe(topic,0);
                return mqttClient;
            } catch (MqttException e) {
                log.error("MQTT connect exception,connect time = " + i);
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e1) {
                    e1.printStackTrace();
                }
            }
        }
        return mqttClient;
    }

}
  • 客戶端
import lombok.extern.slf4j.Slf4j;
import org.eclipse.paho.client.mqttv3.*;
import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence;
import org.springframework.util.ObjectUtils;

@Slf4j
public class MyMqttClient {
    private static MqttClient client;
    private String host;
    private String clientId;
    private String username;
    private String password;
    private Integer timeout;
    private Integer keepAlive;
    private MyHandle myHandle;

    public  MyMqttClient(){
        System.out.println("MyMqttClient空構造函數");
    }

    public MyMqttClient(String host, String username, String password, String clientId, Integer timeOut, Integer keepAlive,MyHandle myHandle) {
        System.out.println("MyMqttClient全參構造");
        this.host = host;
        this.username = username;
        this.password = password;
        this.clientId = clientId;
        this.timeout = timeOut;
        this.keepAlive = keepAlive;
        this.myHandle = myHandle;
    }

    public static MqttClient getClient() {
        return client;
    }

    public static void setClient(MqttClient client) {
        MyMqttClient.client = client;
    }

    /**
     * 設置mqtt連接參數
     */
     public MqttConnectOptions setMqttConnectOptions(String username,String password,Integer timeout, Integer keepAlive){
         MqttConnectOptions options = new MqttConnectOptions();
         options.setUserName(username);
         options.setPassword(password.toCharArray());
         options.setConnectionTimeout(timeout);
         options.setKeepAliveInterval(keepAlive);
         options.setCleanSession(true);
         options.setAutomaticReconnect(true);
         return options;
     }

    /**
     * 連接mqtt服務端
     */
    public void connect() throws MqttException {
        if(client == null){
            client = new MqttClient(host,clientId,new MemoryPersistence());
            client.setCallback(new MyMqttCallback(MyMqttClient.this,this.myHandle));
        }
        MqttConnectOptions mqttConnectOptions = setMqttConnectOptions(username, password, timeout, keepAlive);
        if(!client.isConnected()){
            client.connect(mqttConnectOptions);
        }else{
            client.disconnect();
            client.connect(mqttConnectOptions);
        }
        log.info("MQTT connect success");
    }

    /**
     * 斷開連接
     * @throws MqttException
     */
    public void disconnect()throws MqttException{
        if(null!=client && client.isConnected()){
            client.disconnect();;
        }
    }
    /**
     * 發佈,qos預設為0,非持久化
     */
     public void publish(String pushMessage,String topic,int qos){
         publish(pushMessage, topic, qos, false);
     }

    /**
     * 發佈消息
     *
     * @param pushMessage
     * @param topic
     * @param qos
     * @param retained:留存
     */
    public void publish(String pushMessage, String topic, int qos, boolean retained) {
        MqttMessage mqttMessage = new MqttMessage();
        mqttMessage.setPayload(pushMessage.getBytes());
        mqttMessage.setQos(qos);
        mqttMessage.setRetained(retained);
        MqttTopic mqttTopic = MyMqttClient.getClient().getTopic(topic);
        if(ObjectUtils.isEmpty(mqttTopic)){
            log.error("主題不存在");
        }
        synchronized (this){
            try{
                MqttDeliveryToken mqttDeliveryToken = mqttTopic.publish(mqttMessage);
                mqttDeliveryToken.waitForCompletion(1000L);
            }catch (MqttException e){
                e.printStackTrace();
            }
        }
    }

    /**
     * 訂閱
     *
     * @param topic
     * @param qos
     */
    public void subscribe(String topic, int qos) {
        try {
            MyMqttClient.getClient().subscribe(topic, qos);
            log.info("訂閱主題:"+topic+"成功!");
        } catch (MqttException e) {
            log.error("訂閱主題:"+topic+"失敗!",e);
        }
    }
    /**
     * 取消訂閱
     */
    public void cleanTopic(String topic){
        if(!ObjectUtils.isEmpty(client) && client.isConnected()){
            try{
                client.unsubscribe(topic);
            }catch (MqttException e){
                log.error("取消訂閱失敗!"+e);
            }
        }else{
            log.info("主題不存在或未連接!");
        }
    }
}
  • 回調類(消息發送和接收時響應)
@Slf4j
public class MyMqttCallback implements MqttCallbackExtended {
    private MyMqttClient myMqttClient;
    private MyHandle myHandle;
    public MyMqttCallback(MyMqttClient myMqttClient,MyHandle myHandle) {
        this.myMqttClient = myMqttClient;
        this.myHandle = myHandle;
    }

    /**
     * 連接完成
     * @param reconnect
     * @param serverURI
     */
    @Override
    public void connectComplete(boolean reconnect,String serverURI) {
        log.info("MQTT 連接成功,連接方式:{}",reconnect?"重連":"直連");
        //訂閱主題(可以在這裡訂閱主題)
        try {
            MyMqttClient.getClient().subscribe("topic1");
        } catch (MqttException e) {
            log.error("主題訂閱失敗");
        }
    }

    /**
     * 連接丟失 進行重連操作
     * @param throwable
     */
    @Override
    public void connectionLost(Throwable throwable) {
        log.warn("mqtt connectionLost >>> 5S之後嘗試重連: {}", throwable.getMessage());
        long reconnectTimes = 1;
        while (true){
            try{
                Thread.sleep(5000);
            }catch (InterruptedException ignored){}
            try{
                if(MyMqttClient.getClient().isConnected()){ // 已連接
                    return;
                }
                reconnectTimes+=1;
                log.warn("mqtt reconnect times = {} try again...  mqtt重新連接時間 {}", reconnectTimes, reconnectTimes);
                MyMqttClient.getClient().reconnect();
            }catch (MqttException e){
                log.error("mqtt斷鏈異常",e);
            }
        }
    }

    /**
     * 訂閱者收到消息之後執行
     * @param topic
     * @param mqttMessage
     * @throws Exception
     */
    @Override
    public void messageArrived(String topic, MqttMessage mqttMessage) throws Exception {
        log.info("接收消息主題 : {},接收消息內容 : {}", topic, new String(mqttMessage.getPayload()));
        myHandle.handle(topic,mqttMessage);
    }

    /**
     * * 消息到達後
     * subscribe後,執行的回調函數
     * publish後,配送完成後回調的方法
     *
     * @param iMqttDeliveryToken
     */
    @Override
    public void deliveryComplete(IMqttDeliveryToken iMqttDeliveryToken) {
        System.out.println("接收到已經發佈的 QoS 1 或 QoS 2 消息的傳遞令牌時調用");
        log.info("==========deliveryComplete={}==========", iMqttDeliveryToken.isComplete());
    }
}
import lombok.extern.slf4j.Slf4j;
import org.eclipse.paho.client.mqttv3.MqttMessage;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

@Service
@Slf4j
public class MyHandle {
    @Async
    public void handle(String topic, MqttMessage message) {
        log.info("處理消息主題:" + topic + " 信息:" + message);
    }
}


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 本文分享自華為雲社區《面試必問 | 聊聊MySQL三大核心日誌的實現原理?》,作者:冰 河。 MySQL幾乎成為互聯網行業使用的最多的開源關係型資料庫,正因如此,MySQL也成為各大互聯網公司面試中必問的資料庫,尤其是MySQL中的事務實現機制和三大核心日誌的實現原理。 今天,我們就重點聊聊MySQ ...
  • 近期,Apache DolphinScheduler 社區激動地宣佈 3.2.1 版本的發佈。此次更新不僅著力解決了前一版本(3.2.0)中遺留的問題,而且引入了一系列的功能增強和優化措施。 原先的問題主要源於部分重要代碼在發佈過程中未能成功合併(cherry-pick),加之這部分代碼的合併過程較 ...
  • 這裡給大家分享我在網上總結出來的一些知識,希望對大家有所幫助 一、什麼是SPA SPA(single-page application),翻譯過來就是單頁應用SPA是一種網路應用程式或網站的模型,它通過動態重寫當前頁面來與用戶交互,這種方法避免了頁面之間切換打斷用戶體驗在單頁應用中,所有必要的代碼( ...
  • 訂單履約系統用來管理從接到銷售訂單,到把貨品送到客戶手中的整個業務過程。它是上游交易(如銷售和客戶下單環節)和下游倉儲配送(如庫存管理、物流)之間的橋梁,確保信息流的順暢和操作的協同,提升整個供應鏈的效率和響應速度。 ...
  • 概括 這是一道PHP反序列化的CTF賽題,本意是想用這道題對PHP反序列化進行一定的學習。 過程 我們打開賽題,看看內容 沒有發現什麼東西,看看他的頁面代碼 根據他的提示,感覺是存在一個robots.txt文件的,嘗試訪問一下。 進去看看。 果然如此 我們來分析一下這段代碼 <?php error_ ...
  • 我們在《SqlSugar開發框架》中,有時候都會根據一些需要引入一些設計模式,主要的目的是為瞭解決問題提供便利和代碼重用等目的。而不是為用而用,我們的目的是解決問題,併在一定的場景下以水到渠成的方式處理。不過引入任何的設計模式,都會增加一定的學習難度,除非是自己本身領會比較好了,就會顯得輕鬆一些。本... ...
  • 一、OpenAL的原理和基本概念: 1.1 OpenAL的架構 OpenAL的架構同樣基於三個核心組件:Context(上下文)、Source(聲源)和Buffer(緩衝區)。Context代表了音頻處理的環境,Source是具體的音頻播放源,而Buffer則用於存儲音頻數據。 1.2 音頻渲染流程 ...
  • Admin3 —— 一個輕巧的後臺管理框架,項目後端基於 Java17、SpringBoot3.0,前端基於 TypeScript、Vite3、Vue3、Element Plus,提供登錄會話、用戶管理、角色管理、許可權資源管理、事件日誌、對象存儲等基礎功能。 ...
一周排行
    -Advertisement-
    Play Games
  • 下麵是一個標準的IDistributedCache用例: public class SomeService(IDistributedCache cache) { public async Task<SomeInformation> GetSomeInformationAsync (string na ...
  • 這個庫提供了在啟動期間實例化已註冊的單例,而不是在首次使用它時實例化。 單例通常在首次使用時創建,這可能會導致響應傳入請求的延遲高於平時。在註冊時創建實例有助於防止第一次Request請求的SLA 以往我們要在註冊的時候實例單例可能會這樣寫: //註冊: services.AddSingleton< ...
  • 最近公司的很多項目都要改單點登錄了,不過大部分都還沒敲定,目前立刻要做的就只有一個比較老的項目 先改一個試試手,主要目標就是最短最快實現功能 首先因為要保留原登錄方式,所以頁面上的改動就是在原來登錄頁面下加一個SSO登錄入口 用超鏈接寫的入口,頁面改造後如下圖: 其中超鏈接的 href="Staff ...
  • Like運算符很好用,特別是它所提供的其中*、?這兩種通配符,在Windows文件系統和各類項目中運用非常廣泛。 但Like運算符僅在VB中支持,在C#中,如何實現呢? 以下是關於LikeString的四種實現方式,其中第四種為Regex正則表達式實現,且在.NET Standard 2.0及以上平... ...
  • 一:背景 1. 講故事 前些天有位朋友找到我,說他們的程式記憶體會偶發性暴漲,自己分析了下是非托管記憶體問題,讓我幫忙看下怎麼回事?哈哈,看到這個dump我還是非常有興趣的,居然還有這種游戲幣自助機類型的程式,下次去大玩家看看他們出幣的機器後端是不是C#寫的?由於dump是linux上的程式,剛好win ...
  • 前言 大家好,我是老馬。很高興遇到你。 我們為 java 開發者實現了 java 版本的 nginx https://github.com/houbb/nginx4j 如果你想知道 servlet 如何處理的,可以參考我的另一個項目: 手寫從零實現簡易版 tomcat minicat 手寫 ngin ...
  • 上一次的介紹,主要圍繞如何統一去捕獲異常,以及為每一種異常添加自己的Mapper實現,並且我們知道,當在ExceptionMapper中返回非200的Response,不支持application/json的響應類型,而是寫死的text/plain類型。 Filter為二方包異常手動捕獲 參考:ht ...
  • 大家好,我是R哥。 今天分享一個爽飛了的面試輔導 case: 這個杭州兄弟空窗期 1 個月+,面試了 6 家公司 0 Offer,不知道問題出在哪,難道是杭州的 IT 崩盤了麽? 報名面試輔導後,經過一個多月的輔導打磨,現在成功入職某上市公司,漲薪 30%+,955 工作制,不咋加班,還不捲。 其他 ...
  • 引入依賴 <!--Freemarker wls--> <dependency> <groupId>org.freemarker</groupId> <artifactId>freemarker</artifactId> <version>2.3.30</version> </dependency> ...
  • 你應如何運行程式 互動式命令模式 開始一個互動式會話 一般是在操作系統命令行下輸入python,且不帶任何參數 系統路徑 如果沒有設置系統的PATH環境變數來包括Python的安裝路徑,可能需要機器上Python可執行文件的完整路徑來代替python 運行的位置:代碼位置 不要輸入的內容:提示符和註 ...