概叙
科普文:软件架构网络系列之【应用层协议: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 Mosquitto | test.mosquitto.org | 1883 (TCP) 8883 (SSL) 8080 (WebSocket) | MQTT 3.1.1 / 5.0 | 由 Eclipse 基金会维护,适合测试 |
HiveMQ Public | broker.hivemq.com | 1883 (TCP) 8000 (WebSocket) | MQTT 3.1.1 | 支持 WebSocket,适合 Web 应用 |
EMQX Public | broker.emqx.io | 1883 (TCP) 8083 (WebSocket) | MQTT 3.1.1 / 5.0 | 支持 MQTT 5.0 |
MQTTX Broker | broker.mqttx.io | 1883 (TCP) 8084 (WebSocket) | MQTT 3.1.1 | 由 MQTTX 团队提供 |
Shiftr.io | public.cloud.shiftr.io | 1883 (TCP) 8883 (SSL) | MQTT 3.1.1 | 需注册免费账号 |
Adafruit IO | io.adafruit.com | 1883 (TCP) 8883 (SSL) | MQTT 3.1.1 | 需 API Key(免费额度) |
MQTT V3工具类:MqttClientV3Util
这段代码是一个MQTT 3.0客户端工具类,用于创建、连接和关闭MQTT客户端。主要功能包括:
- 提供默认或自定义参数创建MqttClient实例。
- 使用默认配置连接到MQTT broker。
- 断开并关闭客户端连接,确保资源释放。
实战:软件架构网络系列之【 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()
控制执行顺序(简化同步逻辑,实际项目建议用更严谨的同步机制)。