1. 简介
-
MQTT
MQTT 是物联网 (IoT) 的 OASIS 标准消息传递协议。它被设计为一种极其轻量级的发布/订阅消息传递传输
非常适合以较小的代码占用空间和最小的网络带宽连接远程设备。
发布订阅架构:
MQTT 的核心是MQTT 代理和 MQTT 客户端。MQTT 代理是发送者和接收者之间的中介,负责将消息分发给适当的接收者。MQTT 客户端向代理发布消息,其他客户端则订阅特定主题以接收消息。每条 MQTT 消息都包含一个主题,客户端会订阅自己感兴趣的主题。MQTT 代理会维护一个订阅者列表,并使用该列表将消息传递给相关客户端。
MQTT 代理还可以为断开连接的客户端缓冲消息,确保即使在网络不稳定的情况下也能可靠地传递消息。为了实现这一点,MQTT 支持三种不同的服务质量(QoS) 级别:0(最多一次)、1(至少一次)和 2(恰好一次)。
MQTT 规范有两个版本:MQTT 3.1.1 和 MQTT 5。虽然大多数商业 MQTT 代理现在都支持 MQTT 5,但一些 IoT 管理云服务仍然主要支持 MQTT 3.1.1。
其他的相关特性以及使用说明可参考:MQTT从入门到入土
-
EMQX
EMQX 是一款开源的大规模分布式 MQTT 消息服务器,功能丰富,专为物联网和实时通信应用而设计。EMQX 5.0 单集群支持 MQTT 并发连接数高达 1 亿条,单服务器的传输与处理吞吐量可达每秒百万级 MQTT 消息,同时保证毫秒级的低时延。
EMQX 支持多种协议,包括 MQTT (3.1、3.1.1 和 5.0)、HTTP、QUIC 和 WebSocket 等,保证各种网络环境和硬件设备的可访问性。EMQX 还提供了全面的 SSL/TLS 功能支持,比如双向认证以及多种身份验证机制,为物联网设备和应用程序提供可靠和高效的通信基础设施。
相关特性介绍可参考:EMQX指南
2. 实例
2.1 目标
通过Java+MQTT 5.0协议发送消息到emqx,客户端订阅主题获取到该条消息做处理逻辑。
2.2 tool
工具 | 版本 |
JDK | 22.0.2 |
MQTT | v5 |
Maven | v3.6.3 |
2.3 相关依赖
<properties>
<maven.compiler.source>22</maven.compiler.source>
<maven.compiler.target>22</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependency>
<groupId>org.eclipse.paho</groupId>
<artifactId>org.eclipse.paho.mqttv5.client</artifactId>
<version>1.2.5</version>
</dependency>
2.4 源码
package org.example;
import org.eclipse.paho.mqttv5.client.*;
import org.eclipse.paho.mqttv5.common.MqttException;
import org.eclipse.paho.mqttv5.common.MqttMessage;
import org.eclipse.paho.mqttv5.common.packet.MqttProperties;
/**
* Author:yang
* Date:2024-08-09 9:31
*/
public class Main {
private static final String BROKER_URL = "tcp://110.41.55.242:1883";
private static final String TOPIC = "thing/product/data";
private MqttClient sampleClient;
public static void main(String[] args) {
Main test = new Main();
try {
test.connectAndTest();
} catch (MqttException e) {
throw new RuntimeException(e);
}
}
public void connectAndTest() throws MqttException {
sampleClient = new MqttClient(BROKER_URL, "sample-client");
try {
// 设置连接选项
MqttConnectionOptions options = new MqttConnectionOptions();
options.setCleanStart(false);
// 连接到MQTT代理服务器
sampleClient.connect(options);
// 创建回调接口
MqttCallback callback = new MqttCallback() {
@Override
public void disconnected(MqttDisconnectResponse mqttDisconnectResponse) {
System.out.println("Connection lost: " + mqttDisconnectResponse.getReasonString());
}
@Override
public void mqttErrorOccurred(MqttException e) {
System.out.println("MQTT error occurred: " + e.getMessage());
}
@Override
public void messageArrived(String topic, MqttMessage message) throws Exception {
System.out.println("Received message: " + new String(message.getPayload()) + " on topic: " + topic);
}
@Override
public void deliveryComplete(IMqttToken iMqttToken) {
}
@Override
public void connectComplete(boolean b, String s) {
System.out.println("Connected to MQTT broker: " + s);
// 订阅主题
try {
sampleClient.subscribe(TOPIC,1);
} catch (MqttException e) {
throw new RuntimeException(e);
}
System.out.println("Subscribed to topic: " + TOPIC);
}
@Override
public void authPacketArrived(int i, MqttProperties mqttProperties) {
}
};
// 设置回调
sampleClient.setCallback(callback);
// 取消订阅
sampleClient.unsubscribe(TOPIC);
System.out.println("Unsubscribed from topic: " + TOPIC);
// 订阅主题
sampleClient.subscribe(TOPIC,1);
System.out.println("Subscribed to topic: " + TOPIC);
//启动状态监控线程
Thread statusMonitorThread = new Thread(this::monitorClientStatus);
statusMonitorThread.start();
//主线程将一直运行,保持应用程序活跃
while (true) {
// 这里可以执行其他任务,或者简单地休眠
Thread.sleep(15000);
//sampleClient.disconnectForcibly(2);
System.out.println("close client");
sampleClient.close(true);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
// 确保客户端关闭
if (sampleClient != null && sampleClient.isConnected()) {
try {
sampleClient.disconnect();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
private void monitorClientStatus() {
while (!Thread.currentThread().isInterrupted()) {
try {
System.out.println("MQTT client status: " + sampleClient.isConnected());
// 检查MQTT客户端是否连接
if (!sampleClient.isConnected()) {
System.out.println("MQTT client is not connected, attempting to reconnect.");
// 尝试重新连接
try {
sampleClient.connect();
} catch (MqttException e) {
System.out.println("Failed to reconnect: " + e.getMessage());
// 可以选择在这里处理重连失败的情况,例如重试或退出
}
}
// 适当休眠,避免CPU过载
Thread.sleep(10000);
} catch (InterruptedException e) {
// 线程被中断,退出循环
Thread.currentThread().interrupt();
} catch (Exception e) {
System.out.println("Error while monitoring MQTT client status: " + e.getMessage());
}
}
}
}
2.5 注意事项
-
MQTT v3 和MQTT v5 在Java中API有点差异,更换协议某些接口代码也需要换;
-
订阅主题,获取主题中的消息可以通过回调函数实现(messageArrived);
-
当连接断开再次连接成功后,订阅的TOPIC清除了,需要重新订阅,可以通过(connectComplete,该方法在第一次连接后不会执行);
-
如果需要从topic1发送的消息,通过topic2收到,中间需要做其他的操作,比如规则转换。
注:这个示例可以结合文件切片实现跨网文件传输