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
  • 1、預覽地址:http://139.155.137.144:9012 2、qq群:801913255 一、前言 隨著網路的發展,企業對於信息系統數據的保密工作愈發重視,不同身份、角色對於數據的訪問許可權都應該大相徑庭。 列如 1、不同登錄人員對一個數據列表的可見度是不一樣的,如數據列、數據行、數據按鈕 ...
  • 前言 上一篇文章寫瞭如何使用RabbitMQ做個簡單的發送郵件項目,然後評論也是比較多,也是準備去學習一下如何確保RabbitMQ的消息可靠性,但是由於時間原因,先來說說設計模式中的簡單工廠模式吧! 在瞭解簡單工廠模式之前,我們要知道C#是一款面向對象的高級程式語言。它有3大特性,封裝、繼承、多態。 ...
  • Nodify學習 一:介紹與使用 - 可樂_加冰 - 博客園 (cnblogs.com) Nodify學習 二:添加節點 - 可樂_加冰 - 博客園 (cnblogs.com) 介紹 Nodify是一個WPF基於節點的編輯器控制項,其中包含一系列節點、連接和連接器組件,旨在簡化構建基於節點的工具的過程 ...
  • 創建一個webapi項目做測試使用。 創建新控制器,搭建一個基礎框架,包括獲取當天日期、wiki的請求地址等 創建一個Http請求幫助類以及方法,用於獲取指定URL的信息 使用http請求訪問指定url,先運行一下,看看返回的內容。內容如圖右邊所示,實際上是一個Json數據。我們主要解析 大事記 部 ...
  • 最近在不少自媒體上看到有關.NET與C#的資訊與評價,感覺大家對.NET與C#還是不太瞭解,尤其是對2016年6月發佈的跨平臺.NET Core 1.0,更是知之甚少。在考慮一番之後,還是決定寫點東西總結一下,也回顧一下.NET的發展歷史。 首先,你沒看錯,.NET是跨平臺的,可以在Windows、 ...
  • Nodify學習 一:介紹與使用 - 可樂_加冰 - 博客園 (cnblogs.com) Nodify學習 二:添加節點 - 可樂_加冰 - 博客園 (cnblogs.com) 添加節點(nodes) 通過上一篇我們已經創建好了編輯器實例現在我們為編輯器添加一個節點 添加model和viewmode ...
  • 前言 資料庫併發,數據審計和軟刪除一直是數據持久化方面的經典問題。早些時候,這些工作需要手寫複雜的SQL或者通過存儲過程和觸發器實現。手寫複雜SQL對軟體可維護性構成了相當大的挑戰,隨著SQL字數的變多,用到的嵌套和複雜語法增加,可讀性和可維護性的難度是幾何級暴漲。因此如何在實現功能的同時控制這些S ...
  • 類型檢查和轉換:當你需要檢查對象是否為特定類型,並且希望在同一時間內將其轉換為那個類型時,模式匹配提供了一種更簡潔的方式來完成這一任務,避免了使用傳統的as和is操作符後還需要進行額外的null檢查。 複雜條件邏輯:在處理複雜的條件邏輯時,特別是涉及到多個條件和類型的情況下,使用模式匹配可以使代碼更 ...
  • 在日常開發中,我們經常需要和文件打交道,特別是桌面開發,有時候就會需要載入大批量的文件,而且可能還會存在部分文件缺失的情況,那麼如何才能快速的判斷文件是否存在呢?如果處理不當的,且文件數量比較多的時候,可能會造成卡頓等情況,進而影響程式的使用體驗。今天就以一個簡單的小例子,簡述兩種不同的判斷文件是否... ...
  • 前言 資料庫併發,數據審計和軟刪除一直是數據持久化方面的經典問題。早些時候,這些工作需要手寫複雜的SQL或者通過存儲過程和觸發器實現。手寫複雜SQL對軟體可維護性構成了相當大的挑戰,隨著SQL字數的變多,用到的嵌套和複雜語法增加,可讀性和可維護性的難度是幾何級暴漲。因此如何在實現功能的同時控制這些S ...