实战:软件架构网络系列之【 MQTT基础功能:MQTT 重连(可靠传输保障)】

概叙

科普文:软件架构网络系列之【应用层协议:MQTT(消息队列遥测传输)详解】-CSDN博客

科普文:软件架构网络系列之【在线公共 MQTT 服务器梳理】-CSDN博客

实战:软件架构网络系列之【 MQTT V3工具类:MqttClientV3Util】-CSDN博客

实战:软件架构网络系列之【 MQTT基层功能:发布/订阅】-CSDN博客

实战:软件架构网络系列之【 MQTT基层功能:MQTT遗嘱消息(可靠传输保障)】-CSDN博客

实战:软件架构网络系列之【 MQTT基层功能:带遗嘱消息的智能家居温度传感器(可靠传输保障)】-CSDN博客

实战:软件架构网络系列之【 MQTT基础功能:MQTT over SSL/TLS 安全通信(安全传输)】-CSDN博客

【摘要】本文介绍了MQTT协议在物联网中的应用,包括公共MQTT服务器资源、Java客户端工具类开发及可靠传输保障。

主要内容有:1)列举了6个免费公共MQTT服务器及其连接参数;2)提供基于Eclipse Paho的MQTT V3工具类实现,包含客户端创建、连接管理功能;3)重点演示了包含重连机制的MQTT全生命周期验证方案,通过多线程模拟连接、订阅、发布、断连、重连等操作,确保网络异常时的可靠传输。该方案采用最大重试次数(3次)和重连间隔(3秒)的自动恢复机制,使用AtomicBoolean保障线程安全,适合物联网设备通信稳定性验证。

免费公共 MQTT Broker

Broker 名称地址端口协议支持备注
Eclipse Mosquittotest.mosquitto.org1883 (TCP) 8883 (SSL) 8080 (WebSocket)MQTT 3.1.1 / 5.0由 Eclipse 基金会维护,适合测试
HiveMQ Publicbroker.hivemq.com1883 (TCP) 8000 (WebSocket)MQTT 3.1.1支持 WebSocket,适合 Web 应用
EMQX Publicbroker.emqx.io1883 (TCP) 8083 (WebSocket)MQTT 3.1.1 / 5.0支持 MQTT 5.0
MQTTX Brokerbroker.mqttx.io1883 (TCP) 8084 (WebSocket)MQTT 3.1.1由 MQTTX 团队提供
Shiftr.iopublic.cloud.shiftr.io1883 (TCP) 8883 (SSL)MQTT 3.1.1需注册免费账号
Adafruit IOio.adafruit.com1883 (TCP) 8883 (SSL)MQTT 3.1.1需 API Key(免费额度)

MQTT V3工具类:MqttClientV3Util

这段代码是一个MQTT 3.0客户端工具类,用于创建、连接和关闭MQTT客户端。主要功能包括:

  1. 提供默认或自定义参数创建MqttClient实例。
  2. 使用默认配置连接到MQTT broker。
  3. 断开并关闭客户端连接,确保资源释放。

实战:软件架构网络系列之【 MQTT V3工具类:MqttClientV3Util】-CSDN博客

 MQTT基础功能:MQTT 重连(可靠传输保障)

在MQTT中,当客户端与代理(Broker)意外断开连接时,重连逻辑的实现至关重要。

为了实现MQTT重连逻辑的通用化,我们可以采用一个循环重试的机制,并在每次重试之间设置一定的延迟。

下面Java代码演示了MQTT客户端的完整生命周期验证,包括连接、订阅、发布、断开、重连等操作。

通过多线程执行各个步骤,验证MQTT通信的可靠性与重连机制。主要功能封装在[MqttReconnectExample]类中,使用`Eclipse Paho`库实现MQTT协议交互。

package com.zxx.study.rjava.mqtt;

import com.zxx.study.common.util.ZhouxxTool;
import org.eclipse.paho.client.mqttv3.MqttClient;
import org.eclipse.paho.client.mqttv3.MqttException;
import org.eclipse.paho.client.mqttv3.MqttMessage;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;

/**
 * MQTT全流程多线程验证示例
 * 验证MQTT客户端的完整生命周期:连接、发布、订阅、断开连接、重连、发布、订阅
 *
 * @author zhouxx
 * @create 2024-10-10 9:32
 */
public class MqttReconnectExample {

    private static final String BROKER_URL = "tcp://test.mosquitto.org:1883";
    private static final String CLIENT_ID = "ClientReconnectExample";
    private static MqttClient client;
    private static final int MAX_RECONNECT_ATTEMPTS = 3;
    private static final int RECONNECT_INTERVAL = 3000; // 3秒
    private static final AtomicBoolean isConnected = new AtomicBoolean(false);
    private static final String TEST_TOPIC = "test/lifecycle";

    /**
     * 主函数,演示MQTT客户端的完整生命周期验证
     * 1. 初始化客户端并连接
     * 2. 订阅主题
     * 3. 发布消息
     * 4. 断开连接
     * 5. 重新连接
     * 6. 再次发布消息
     * 7. 验证订阅功能
     */
    public static void main(String[] args) {
        ExecutorService executor = Executors.newFixedThreadPool(3);

        try {
            ZhouxxTool.printTimeAndThread("=== 开始MQTT全流程验证 ===");

            // 步骤1: 初始化客户端并连接
            executor.submit(() -> {
                try {
                    ZhouxxTool.printTimeAndThread("步骤1: 初始化客户端并连接");
                    initializeClient();
                    ZhouxxTool.printTimeAndThread("步骤1完成: 客户端初始化并连接成功");
                } catch (Exception e) {
                    ZhouxxTool.printTimeAndThread("步骤1失败: " + e.getMessage());
                }
            });

            // 等待初始化完成
            Thread.sleep(2000);

            // 步骤2: 订阅主题
            executor.submit(() -> {
                try {
                    ZhouxxTool.printTimeAndThread("步骤2: 订阅主题");
                    if (isConnected.get()) {
                        subscribeToTopics();
                        ZhouxxTool.printTimeAndThread("步骤2完成: 主题订阅成功");
                    } else {
                        ZhouxxTool.printTimeAndThread("步骤2失败: 客户端未连接,无法订阅");
                    }
                } catch (Exception e) {
                    ZhouxxTool.printTimeAndThread("步骤2失败: " + e.getMessage());
                }
            });

            // 等待订阅完成
            Thread.sleep(2000);

            // 步骤3: 发布消息
            executor.submit(() -> {
                try {
                    ZhouxxTool.printTimeAndThread("步骤3: 发布消息");
                    if (isConnected.get()) {
                        publishTestMessage();
                        ZhouxxTool.printTimeAndThread("步骤3完成: 消息发布成功");
                    } else {
                        ZhouxxTool.printTimeAndThread("步骤3失败: 客户端未连接,无法发布");
                    }
                } catch (Exception e) {
                    ZhouxxTool.printTimeAndThread("步骤3失败: " + e.getMessage());
                }
            });

            // 等待消息发布完成
            Thread.sleep(3000);

            // 步骤4: 断开连接
            executor.submit(() -> {
                try {
                    ZhouxxTool.printTimeAndThread("步骤4: 断开连接");
                    disconnectClient();
                    ZhouxxTool.printTimeAndThread("步骤4完成: 客户端断开连接");
                } catch (Exception e) {
                    ZhouxxTool.printTimeAndThread("步骤4失败: " + e.getMessage());
                }
            });

            // 等待断开连接完成
            Thread.sleep(2000);

            // 步骤5: 重新连接
            executor.submit(() -> {
                try {
                    ZhouxxTool.printTimeAndThread("步骤5: 重新连接");
                    reconnectClient();
                    ZhouxxTool.printTimeAndThread("步骤5完成: 客户端重新连接成功");
                } catch (Exception e) {
                    ZhouxxTool.printTimeAndThread("步骤5失败: " + e.getMessage());
                }
            });

            // 等待重连完成
            Thread.sleep(3000);

            // 步骤6: 再次发布消息
            executor.submit(() -> {
                try {
                    ZhouxxTool.printTimeAndThread("步骤6: 再次发布消息");
                    if (isConnected.get()) {
                        publishTestMessage();
                        ZhouxxTool.printTimeAndThread("步骤6完成: 消息再次发布成功");
                    } else {
                        ZhouxxTool.printTimeAndThread("步骤6失败: 客户端未连接,无法发布");
                    }
                } catch (Exception e) {
                    ZhouxxTool.printTimeAndThread("步骤6失败: " + e.getMessage());
                }
            });

            // 等待消息发布完成
            Thread.sleep(3000);

            // 步骤7: 验证订阅功能
            executor.submit(() -> {
                try {
                    ZhouxxTool.printTimeAndThread("步骤7: 验证订阅功能");
                    if (isConnected.get()) {
                        subscribeToTopics(); // 重新订阅验证
                        ZhouxxTool.printTimeAndThread("步骤7完成: 订阅功能验证成功");
                    } else {
                        ZhouxxTool.printTimeAndThread("步骤7失败: 客户端未连接,无法验证订阅");
                    }
                } catch (Exception e) {
                    ZhouxxTool.printTimeAndThread("步骤7失败: " + e.getMessage());
                }
            });

            // 等待所有任务完成
            Thread.sleep(5000);

            ZhouxxTool.printTimeAndThread("=== MQTT全流程验证完成 ===");

        } catch (InterruptedException e) {
            ZhouxxTool.printTimeAndThread("主线程被中断");
            Thread.currentThread().interrupt();
        } finally {
            executor.shutdown();
            try {
                if (!executor.awaitTermination(10, TimeUnit.SECONDS)) {
                    executor.shutdownNow();
                }
            } catch (InterruptedException e) {
                executor.shutdownNow();
                Thread.currentThread().interrupt();
            }
            // 最终关闭客户端
            MqttClientV3Util.close(client);
        }
    }

    /**
     * 初始化MQTT客户端
     */
    private static void initializeClient() throws MqttException {
        try {
            client = MqttClientV3Util.getMqttClient(BROKER_URL, CLIENT_ID);

            // 设置回调函数
            client.setCallback(new org.eclipse.paho.client.mqttv3.MqttCallback() {
                @Override
                public void connectionLost(Throwable cause) {
                    isConnected.set(false);
                    ZhouxxTool.printTimeAndThread("连接丢失: " + cause.getMessage());
                }

                @Override
                public void messageArrived(String topic, MqttMessage message) throws Exception {
                    ZhouxxTool.printTimeAndThread("收到消息 [" + topic + "]: " + new String(message.getPayload()));
                }

                @Override
                public void deliveryComplete(org.eclipse.paho.client.mqttv3.IMqttDeliveryToken token) {
                    ZhouxxTool.printTimeAndThread("消息发送完成");
                }
            });

            // 首次连接
            connectWithRetry();

        } catch (MqttException e) {
            ZhouxxTool.printTimeAndThread("初始化客户端失败: " + e.getMessage());
            throw e;
        }
    }

    /**
     * 带重试机制的连接方法
     */
    private static void connectWithRetry() {
        int attempts = 0;
        while (attempts < MAX_RECONNECT_ATTEMPTS) {
            try {
                if (client != null && !client.isConnected()) {
                    client.connect();
                    isConnected.set(true);
                    ZhouxxTool.printTimeAndThread("连接成功");

                    break;
                } else if (client != null && client.isConnected()) {
                    isConnected.set(true);
                    ZhouxxTool.printTimeAndThread("客户端已连接");
                    break;
                }
            } catch (MqttException e) {
                attempts++;
                isConnected.set(false);
                ZhouxxTool.printTimeAndThread("连接尝试 " + attempts + " 失败: " + e.getMessage());

                if (attempts < MAX_RECONNECT_ATTEMPTS) {
                    try {
                        Thread.sleep(RECONNECT_INTERVAL);
                    } catch (InterruptedException ie) {
                        Thread.currentThread().interrupt();
                        return;
                    }
                }
            }
        }

        if (!isConnected.get()) {
            ZhouxxTool.printTimeAndThread("达到最大重连次数,连接失败");
        }
    }

    /**
     * 手动重连逻辑
     */
    private static void manualReconnect() {
        ZhouxxTool.printTimeAndThread("执行手动重连...");

        int attempts = 0;
        while (attempts < MAX_RECONNECT_ATTEMPTS && !isConnected.get()) {
            try {
                attempts++;
                ZhouxxTool.printTimeAndThread("重连尝试 " + attempts + "/" + MAX_RECONNECT_ATTEMPTS);

                if (client != null) {
                    // 检查客户端状态
                    if (!client.isConnected()) {
                        client.connect();
                        isConnected.set(true);
                        ZhouxxTool.printTimeAndThread("手动重连成功");
                        break;
                    } else {
                        isConnected.set(true);
                        ZhouxxTool.printTimeAndThread("客户端已连接,无需重连");
                        break;
                    }
                }
            } catch (MqttException e) {
                isConnected.set(false);
                ZhouxxTool.printTimeAndThread("重连尝试 " + attempts + " 失败: " + e.getMessage());

                if (attempts < MAX_RECONNECT_ATTEMPTS) {
                    try {
                        Thread.sleep(RECONNECT_INTERVAL);
                    } catch (InterruptedException ie) {
                        Thread.currentThread().interrupt();
                        return;
                    }
                }
            }
        }

        if (!isConnected.get()) {
            ZhouxxTool.printTimeAndThread("手动重连失败,达到最大重连次数");
        }
    }

    /**
     * 订阅主题
     */
    private static void subscribeToTopics() {
        try {
            if (isConnected.get() && client != null && client.isConnected()) {
                client.subscribe(TEST_TOPIC, 1);
                ZhouxxTool.printTimeAndThread("已订阅主题: " + TEST_TOPIC);
            } else {
                ZhouxxTool.printTimeAndThread("无法订阅主题: 客户端未连接");
            }
        } catch (MqttException e) {
            ZhouxxTool.printTimeAndThread("订阅主题失败: " + e.getMessage());
        }
    }

    /**
     * 发布测试消息
     */
    private static void publishTestMessage() {
        try {
            if (isConnected.get() && client != null && client.isConnected()) {
                String content = "MQTT全流程测试消息 - 时间戳: " + System.currentTimeMillis();
                MqttMessage message = new MqttMessage(content.getBytes());
                message.setQos(1);
                client.publish(TEST_TOPIC, message);
                ZhouxxTool.printTimeAndThread("发布测试消息: " + content);
            } else {
                ZhouxxTool.printTimeAndThread("无法发布消息: 客户端未连接");
            }
        } catch (MqttException e) {
            ZhouxxTool.printTimeAndThread("发布消息失败: " + e.getMessage());
        }
    }

    /**
     * 断开客户端连接
     */
    private static void disconnectClient() {
        try {
            if (client != null && client.isConnected()) {
                client.disconnect();
                isConnected.set(false);
                ZhouxxTool.printTimeAndThread("客户端已断开连接");
            } else {
                ZhouxxTool.printTimeAndThread("客户端未连接或为空");
            }
        } catch (MqttException e) {
            ZhouxxTool.printTimeAndThread("断开连接失败: " + e.getMessage());
        }
    }

    /**
     * 重新连接客户端
     */
    private static void reconnectClient() {
        try {
            if (client == null) {
                initializeClient();
            } else {
                manualReconnect();
            }
        } catch (Exception e) {
            ZhouxxTool.printTimeAndThread("重新连接失败: " + e.getMessage());
        }
    }
}

本代码是一个完整的 MQTT 客户端生命周期多线程验证工具,通过模拟真实场景中的连接、发布、订阅、断连和重连操作,帮助开发者验证 MQTT 客户端在复杂环境下的稳定性和可靠性,适合用于 MQTT 集成测试或功能验证。

核心功能

1. 多线程操作:通过线程池(ExecutorService)并发执行初始化、订阅、发布、断开、重连等步骤,模拟真实场景中的异步操作。

2. 完整生命周期验证:覆盖 MQTT 客户端从初始化到最终关闭的全流程,包括异常处理(如连接失败、重连机制)。

3. 重连机制:支持自动重试连接(connectWithRetry)和手动重连(manualReconnect),确保网络波动时能恢复连接。

4. 状态管理:通过 AtomicBoolean isConnected 标记当前连接状态,避免在未连接时执行发布/订阅操作。

设计亮点

1. 多线程模拟异步场景:通过线程池并发执行不同步骤,更贴近实际应用中多线程操作 MQTT 客户端的情况。

2. 重连机制健壮性:自动重试和手动重连结合,应对网络波动或临时故障。

3. 状态安全控制:使用 AtomicBoolean 确保连接状态的线程安全,避免未连接时误操作。

4. 生命周期完整性:覆盖从初始化到关闭的全流程,验证各环节的可靠性。

核心方法详解

(1) 初始化客户端(initializeClient)

  • 创建 MQTT 客户端实例(通过工具类 MqttClientV3Util,推测封装了 MqttClient 的初始化逻辑)。
  • 设置回调函数(MqttCallback),处理以下事件:
  • 连接丢失connectionLost):标记 isConnected=false,打印错误信息。
  • 消息到达messageArrived):打印接收到的消息内容(主题+消息体)。
  • 消息发送完成deliveryComplete):打印发送成功日志。
  • 调用 connectWithRetry() 实现带重试的初始连接。

(2) 带重试的连接(connectWithRetry)

  • 最多重试 MAX_RECONNECT_ATTEMPTS 次(默认 3 次),每次间隔 RECONNECT_INTERVAL(3 秒)。
  • 若客户端未连接,则尝试调用 client.connect();成功后标记 isConnected=true
  • 若达到最大重试次数仍失败,打印错误日志。

(3) 手动重连(manualReconnect)

  • 与 connectWithRetry 类似,但用于断开后主动恢复连接的场景(步骤 5)。
  • 检查客户端状态,若未连接则尝试重新连接。

(4) 订阅主题(subscribeToTopics)

  • 仅在连接状态下订阅 TEST_TOPIC(QoS=1),失败时打印错误信息。

(5) 发布消息(publishTestMessage)

  • 构造包含当前时间戳的测试消息(QoS=1),仅在连接状态下发布到 TEST_TOPIC

(6) 断开连接(disconnectClient)

  • 若已连接,则调用 client.disconnect() 并标记 isConnected=false

(7) 重新连接(reconnectClient)

  • 若客户端未初始化,则调用 initializeClient();否则调用 manualReconnect() 恢复连接。

(8) 资源清理

  • 在 finally 块中关闭线程池(executor.shutdown()),并调用工具类方法 MqttClientV3Util.close(client) 释放 MQTT 客户端资源(推测包含 client.close() 逻辑)。

主流程(main 方法)

通过线程池提交多个任务,按顺序验证以下步骤:

1. 初始化并连接客户端(步骤1):调用 initializeClient() 创建 MQTT 客户端并尝试连接。

2. 订阅主题(步骤2):若已连接,则订阅 TEST_TOPIC(QoS=1,保证消息至少送达一次)。

3. 发布消息(步骤3):若已连接,发布一条包含时间戳的测试消息到 TEST_TOPIC

4. 断开连接(步骤4):主动断开与代理的连接。

5. 重新连接(步骤5):调用 reconnectClient() 恢复连接。

6. 再次发布消息(步骤6):重连成功后,再次发布消息验证连接有效性。

7. 验证订阅功能(步骤7):重新订阅主题,确保订阅功能正常。

每个步骤通过 Thread.sleep() 控制执行顺序(简化同步逻辑,实际项目建议用更严谨的同步机制)。

运行结果

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

01Byte空间

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值