Commit 69e25eeaaccfa6aa81b252ea4de7c7159fe5aec0

Authored by haohao
1 parent 8b4bee69

reactor:【IoT 物联网】重构 EMQX 协议处理逻辑,新增认证和下行消息处理器

Showing 7 changed files with 263 additions and 242 deletions
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/config/IotGatewayConfiguration.java
1 1 package cn.iocoder.yudao.module.iot.gateway.config;
2 2  
3 3 import cn.iocoder.yudao.module.iot.core.messagebus.core.IotMessageBus;
  4 +import cn.iocoder.yudao.module.iot.gateway.protocol.emqx.IotEmqxAuthEventProtocol;
  5 +import cn.iocoder.yudao.module.iot.gateway.protocol.emqx.IotEmqxDownstreamSubscriber;
  6 +import cn.iocoder.yudao.module.iot.gateway.protocol.emqx.IotEmqxUpstreamProtocol;
4 7 import cn.iocoder.yudao.module.iot.gateway.protocol.http.IotHttpDownstreamSubscriber;
5 8 import cn.iocoder.yudao.module.iot.gateway.protocol.http.IotHttpUpstreamProtocol;
6   -import cn.iocoder.yudao.module.iot.gateway.protocol.mqtt.IotMqttDownstreamSubscriber;
7   -import cn.iocoder.yudao.module.iot.gateway.protocol.mqtt.IotMqttUpstreamProtocol;
8 9 import lombok.extern.slf4j.Slf4j;
9 10 import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
10 11 import org.springframework.boot.context.properties.EnableConfigurationProperties;
... ... @@ -37,7 +38,7 @@ public class IotGatewayConfiguration {
37 38 }
38 39  
39 40 /**
40   - * IoT 网关 MQTT 协议配置类
  41 + * IoT 网关 EMQX 协议配置类
41 42 */
42 43 @Configuration
43 44 @ConditionalOnProperty(prefix = "yudao.iot.gateway.protocol.emqx", name = "enabled", havingValue = "true")
... ... @@ -45,14 +46,19 @@ public class IotGatewayConfiguration {
45 46 public static class MqttProtocolConfiguration {
46 47  
47 48 @Bean
48   - public IotMqttUpstreamProtocol iotMqttUpstreamProtocol(IotGatewayProperties gatewayProperties) {
49   - return new IotMqttUpstreamProtocol(gatewayProperties.getProtocol().getEmqx());
  49 + public IotEmqxAuthEventProtocol iotEmqxAuthEventProtocol(IotGatewayProperties gatewayProperties) {
  50 + return new IotEmqxAuthEventProtocol(gatewayProperties.getProtocol().getEmqx());
50 51 }
51 52  
52 53 @Bean
53   - public IotMqttDownstreamSubscriber iotMqttDownstreamSubscriber(IotMqttUpstreamProtocol mqttUpstreamProtocol,
  54 + public IotEmqxUpstreamProtocol iotEmqxUpstreamProtocol(IotGatewayProperties gatewayProperties) {
  55 + return new IotEmqxUpstreamProtocol(gatewayProperties.getProtocol().getEmqx());
  56 + }
  57 +
  58 + @Bean
  59 + public IotEmqxDownstreamSubscriber iotEmqxDownstreamSubscriber(IotEmqxUpstreamProtocol mqttUpstreamProtocol,
54 60 IotMessageBus messageBus) {
55   - return new IotMqttDownstreamSubscriber(mqttUpstreamProtocol, messageBus);
  61 + return new IotEmqxDownstreamSubscriber(mqttUpstreamProtocol, messageBus);
56 62 }
57 63 }
58 64  
... ...
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/emqx/IotEmqxAuthEventProtocol.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.gateway.protocol.emqx;
  2 +
  3 +import cn.iocoder.yudao.module.iot.core.util.IotDeviceMessageUtils;
  4 +import cn.iocoder.yudao.module.iot.gateway.config.IotGatewayProperties;
  5 +import cn.iocoder.yudao.module.iot.gateway.protocol.emqx.router.IotEmqxAuthEventHandler;
  6 +import cn.iocoder.yudao.module.iot.gateway.util.IotMqttTopicUtils;
  7 +import io.vertx.core.Vertx;
  8 +import io.vertx.core.http.HttpServer;
  9 +import io.vertx.ext.web.Router;
  10 +import io.vertx.ext.web.handler.BodyHandler;
  11 +import jakarta.annotation.PostConstruct;
  12 +import jakarta.annotation.PreDestroy;
  13 +import lombok.extern.slf4j.Slf4j;
  14 +
  15 +/**
  16 + * IoT 网关 EMQX 认证事件协议服务
  17 + * <p>
  18 + * 为 EMQX 提供 HTTP 接口服务,包括:
  19 + * 1. 设备认证接口 - 对应 EMQX HTTP 认证插件
  20 + * 2. 设备事件处理接口 - 对应 EMQX Webhook 事件通知
  21 + *
  22 + * @author 芋道源码
  23 + */
  24 +@Slf4j
  25 +public class IotEmqxAuthEventProtocol {
  26 +
  27 + private final IotGatewayProperties.EmqxProperties emqxProperties;
  28 +
  29 + private final String serverId;
  30 +
  31 + private Vertx vertx;
  32 +
  33 + private HttpServer httpServer;
  34 +
  35 + public IotEmqxAuthEventProtocol(IotGatewayProperties.EmqxProperties emqxProperties) {
  36 + this.emqxProperties = emqxProperties;
  37 + this.serverId = IotDeviceMessageUtils.generateServerId(emqxProperties.getMqttPort());
  38 + }
  39 +
  40 + @PostConstruct
  41 + public void start() {
  42 + try {
  43 + // 创建 Vertx 实例
  44 + this.vertx = Vertx.vertx();
  45 + startHttpServer();
  46 + log.info("[start][IoT 网关 EMQX 认证事件协议服务启动成功, 端口: {}]", emqxProperties.getHttpPort());
  47 + } catch (Exception e) {
  48 + log.error("[start][IoT 网关 EMQX 认证事件协议服务启动失败]", e);
  49 + throw e;
  50 + }
  51 + }
  52 +
  53 + @PreDestroy
  54 + public void stop() {
  55 + stopHttpServer();
  56 +
  57 + // 关闭 Vertx 实例
  58 + if (vertx != null) {
  59 + try {
  60 + vertx.close();
  61 + log.debug("[stop][Vertx 实例已关闭]");
  62 + } catch (Exception e) {
  63 + log.warn("[stop][关闭 Vertx 实例失败]", e);
  64 + }
  65 + }
  66 +
  67 + log.info("[stop][IoT 网关 EMQX 认证事件协议服务已停止]");
  68 + }
  69 +
  70 + /**
  71 + * 启动 HTTP 服务器
  72 + */
  73 + private void startHttpServer() {
  74 + int port = emqxProperties.getHttpPort();
  75 +
  76 + // 1. 创建路由
  77 + Router router = Router.router(vertx);
  78 + router.route().handler(BodyHandler.create());
  79 +
  80 + // 2. 创建处理器,传入 serverId
  81 + IotEmqxAuthEventHandler handler = new IotEmqxAuthEventHandler(serverId);
  82 + router.post(IotMqttTopicUtils.MQTT_AUTH_PATH).handler(handler::handleAuth);
  83 + router.post(IotMqttTopicUtils.MQTT_EVENT_PATH).handler(handler::handleEvent);
  84 +
  85 + // 3. 启动 HTTP 服务器
  86 + try {
  87 + httpServer = vertx.createHttpServer()
  88 + .requestHandler(router)
  89 + .listen(port)
  90 + .result();
  91 + } catch (Exception e) {
  92 + log.error("[startHttpServer][HTTP 服务器启动失败, 端口: {}]", port, e);
  93 + throw e;
  94 + }
  95 + }
  96 +
  97 + /**
  98 + * 停止 HTTP 服务器
  99 + */
  100 + private void stopHttpServer() {
  101 + if (httpServer == null) {
  102 + return;
  103 + }
  104 +
  105 + try {
  106 + httpServer.close().result();
  107 + log.info("[stopHttpServer][HTTP 服务器已停止]");
  108 + } catch (Exception e) {
  109 + log.error("[stopHttpServer][HTTP 服务器停止失败]", e);
  110 + }
  111 + }
  112 +
  113 +}
0 114 \ No newline at end of file
... ...
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/mqtt/IotMqttDownstreamSubscriber.java renamed to yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/emqx/IotEmqxDownstreamSubscriber.java
1   -package cn.iocoder.yudao.module.iot.gateway.protocol.mqtt;
  1 +package cn.iocoder.yudao.module.iot.gateway.protocol.emqx;
2 2  
3 3 import cn.iocoder.yudao.module.iot.core.messagebus.core.IotMessageBus;
4 4 import cn.iocoder.yudao.module.iot.core.messagebus.core.IotMessageSubscriber;
5 5 import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
6 6 import cn.iocoder.yudao.module.iot.core.util.IotDeviceMessageUtils;
7   -import cn.iocoder.yudao.module.iot.gateway.protocol.mqtt.router.IotMqttDownstreamHandler;
  7 +import cn.iocoder.yudao.module.iot.gateway.protocol.emqx.router.IotEmqxDownstreamHandler;
8 8 import jakarta.annotation.PostConstruct;
9 9 import lombok.extern.slf4j.Slf4j;
10 10  
11 11 /**
12   - * IoT 网关 MQTT 订阅者:接收下行给设备的消息
  12 + * IoT 网关 EMQX 订阅者:接收下行给设备的消息
13 13 *
14 14 * @author 芋道源码
15 15 */
16 16 @Slf4j
17   -public class IotMqttDownstreamSubscriber implements IotMessageSubscriber<IotDeviceMessage> {
  17 +public class IotEmqxDownstreamSubscriber implements IotMessageSubscriber<IotDeviceMessage> {
  18 +
  19 + private final IotEmqxDownstreamHandler downstreamHandler;
18 20  
19   - private final IotMqttDownstreamHandler downstreamHandler;
20 21 private final IotMessageBus messageBus;
21   - private final IotMqttUpstreamProtocol protocol;
22 22  
23   - public IotMqttDownstreamSubscriber(IotMqttUpstreamProtocol protocol, IotMessageBus messageBus) {
  23 + private final IotEmqxUpstreamProtocol protocol;
  24 +
  25 + public IotEmqxDownstreamSubscriber(IotEmqxUpstreamProtocol protocol, IotMessageBus messageBus) {
24 26 this.protocol = protocol;
25 27 this.messageBus = messageBus;
26   - this.downstreamHandler = new IotMqttDownstreamHandler(protocol);
  28 + this.downstreamHandler = new IotEmqxDownstreamHandler(protocol);
27 29 }
28 30  
29 31 @PostConstruct
... ...
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/mqtt/IotMqttUpstreamProtocol.java renamed to yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/emqx/IotEmqxUpstreamProtocol.java
1   -package cn.iocoder.yudao.module.iot.gateway.protocol.mqtt;
  1 +package cn.iocoder.yudao.module.iot.gateway.protocol.emqx;
2 2  
3 3 import cn.hutool.core.collection.CollUtil;
4 4 import cn.iocoder.yudao.module.iot.core.util.IotDeviceMessageUtils;
5 5 import cn.iocoder.yudao.module.iot.gateway.config.IotGatewayProperties;
6   -import cn.iocoder.yudao.module.iot.gateway.protocol.mqtt.router.IotMqttHttpAuthHandler;
7   -import cn.iocoder.yudao.module.iot.gateway.protocol.mqtt.router.IotMqttUpstreamHandler;
8   -import cn.iocoder.yudao.module.iot.gateway.util.IotMqttTopicUtils;
  6 +import cn.iocoder.yudao.module.iot.gateway.protocol.emqx.router.IotEmqxUpstreamHandler;
9 7 import io.netty.handler.codec.mqtt.MqttQoS;
10 8 import io.vertx.core.Vertx;
11 9 import io.vertx.core.buffer.Buffer;
12   -import io.vertx.core.http.HttpServer;
13   -import io.vertx.ext.web.Router;
14   -import io.vertx.ext.web.handler.BodyHandler;
15 10 import io.vertx.mqtt.MqttClient;
16 11 import io.vertx.mqtt.MqttClientOptions;
17 12 import jakarta.annotation.PostConstruct;
... ... @@ -22,24 +17,19 @@ import lombok.extern.slf4j.Slf4j;
22 17 import java.util.HashMap;
23 18 import java.util.List;
24 19 import java.util.Map;
25   -import java.util.concurrent.atomic.AtomicInteger;
  20 +import java.util.concurrent.CountDownLatch;
  21 +import java.util.concurrent.atomic.AtomicBoolean;
26 22  
27 23 /**
28   - * IoT 网关 MQTT 协议:接收设备上行消息
29   - * <p>
30   - * 1. MQTT 客户端:连接 EMQX,消费处理设备上行和下行消息
31   - * 2. HTTP 认证服务:为 EMQX 提供设备认证、连接、断开接口
  24 + * IoT 网关 EMQX 协议:接收设备上行消息
32 25 *
33 26 * @author 芋道源码
34 27 */
35 28 @Slf4j
36   -public class IotMqttUpstreamProtocol {
  29 +public class IotEmqxUpstreamProtocol {
37 30  
38 31 private final IotGatewayProperties.EmqxProperties emqxProperties;
39 32  
40   - /**
41   - * 服务运行状态标志
42   - */
43 33 private volatile boolean isRunning = false;
44 34  
45 35 private Vertx vertx;
... ... @@ -47,14 +37,11 @@ public class IotMqttUpstreamProtocol {
47 37 @Getter
48 38 private final String serverId;
49 39  
50   - // MQTT 客户端相关
51 40 private MqttClient mqttClient;
52   - private IotMqttUpstreamHandler upstreamHandler;
53 41  
54   - // HTTP 认证服务相关
55   - private HttpServer httpAuthServer;
  42 + private IotEmqxUpstreamHandler upstreamHandler;
56 43  
57   - public IotMqttUpstreamProtocol(IotGatewayProperties.EmqxProperties emqxProperties) {
  44 + public IotEmqxUpstreamProtocol(IotGatewayProperties.EmqxProperties emqxProperties) {
58 45 this.emqxProperties = emqxProperties;
59 46 this.serverId = IotDeviceMessageUtils.generateServerId(emqxProperties.getMqttPort());
60 47 }
... ... @@ -62,26 +49,35 @@ public class IotMqttUpstreamProtocol {
62 49 @PostConstruct
63 50 public void start() {
64 51 if (isRunning) {
65   - log.warn("[start][MQTT 协议服务已经在运行中,请勿重复启动]");
66 52 return;
67 53 }
68   - log.info("[start][启动 MQTT 协议服务]");
69 54  
70 55 try {
  56 + // 1. 初始化 Vertx 实例
71 57 this.vertx = Vertx.vertx();
72 58  
73   - // 1. 启动 HTTP 认证服务
74   - startHttpAuthServer();
75   -
76 59 // 2. 启动 MQTT 客户端
77 60 startMqttClient();
78 61  
  62 + // 3. 标记服务为运行状态
79 63 isRunning = true;
80   - log.info("[start][MQTT 协议服务启动完成]");
  64 + log.info("[start][IoT 网关 EMQX 协议启动成功]");
81 65 } catch (Exception e) {
82   - log.error("[start][MQTT 协议服务启动失败]", e);
83   - // 启动失败时清理资源
  66 + log.error("[start][IoT 网关 EMQX 协议服务启动失败,应用将关闭]", e);
84 67 stop();
  68 +
  69 + // 异步关闭应用,避免阻塞当前线程
  70 + new Thread(() -> {
  71 + try {
  72 + Thread.sleep(1000); // 等待1秒让日志输出完成
  73 + log.error("[start][由于 MQTT 连接失败,正在关闭应用]");
  74 + } catch (InterruptedException ex) {
  75 + Thread.currentThread().interrupt();
  76 + } finally {
  77 + System.exit(1); // 直接关闭 JVM
  78 + }
  79 + }).start();
  80 +
85 81 throw e;
86 82 }
87 83 }
... ... @@ -89,95 +85,42 @@ public class IotMqttUpstreamProtocol {
89 85 @PreDestroy
90 86 public void stop() {
91 87 if (!isRunning) {
92   - log.warn("[stop][MQTT 协议服务已经停止,无需再次停止]");
93 88 return;
94 89 }
95   - log.info("[stop][停止 MQTT 协议服务]");
96 90  
97 91 // 1. 停止 MQTT 客户端
98 92 stopMqttClient();
99 93  
100   - // 2. 停止 HTTP 认证服务
101   - stopHttpAuthServer();
102   -
103   - // 3. 关闭 Vertx 实例
  94 + // 2. 关闭 Vertx 实例
104 95 if (vertx != null) {
105 96 try {
106 97 vertx.close();
107   - log.debug("[stop][Vertx 实例已关闭]");
108 98 } catch (Exception e) {
109 99 log.warn("[stop][关闭 Vertx 实例失败]", e);
110 100 }
111 101 }
112 102  
  103 + // 3. 标记服务为停止状态
113 104 isRunning = false;
114   - log.info("[stop][MQTT 协议服务已停止]");
115   - }
116   -
117   - /**
118   - * 启动 HTTP 认证服务
119   - */
120   - private void startHttpAuthServer() {
121   - log.info("[startHttpAuthServer][启动 HTTP 认证服务]");
122   -
123   - // 1.1 创建路由
124   - Router router = Router.router(vertx);
125   - router.route().handler(BodyHandler.create());
126   - // 1.2 创建认证处理器
127   - IotMqttHttpAuthHandler authHandler = new IotMqttHttpAuthHandler(this);
128   - router.post(IotMqttTopicUtils.MQTT_AUTH_PATH).handler(authHandler::handleAuth);
129   - router.post(IotMqttTopicUtils.MQTT_EVENT_PATH).handler(authHandler::handleEvent);
130   -
131   - // 2. 启动 HTTP 服务器
132   - int authPort = emqxProperties.getHttpPort();
133   - try {
134   - httpAuthServer = vertx.createHttpServer()
135   - .requestHandler(router)
136   - .listen(authPort)
137   - .result();
138   - log.info("[startHttpAuthServer][HTTP 认证服务启动成功, 端口: {}]", authPort);
139   - } catch (Exception e) {
140   - log.error("[startHttpAuthServer][HTTP 认证服务启动失败]", e);
141   - throw e;
142   - }
143   - }
144   -
145   - /**
146   - * 停止 HTTP 认证服务
147   - */
148   - private void stopHttpAuthServer() {
149   - if (httpAuthServer == null) {
150   - return;
151   - }
152   - try {
153   - httpAuthServer.close().result();
154   - log.info("[stopHttpAuthServer][HTTP 认证服务已停止]");
155   - } catch (Exception e) {
156   - log.error("[stopHttpAuthServer][HTTP 认证服务停止失败]", e);
157   - }
  105 + log.info("[stop][IoT 网关 MQTT 协议服务已停止]");
158 106 }
159 107  
160 108 /**
161 109 * 启动 MQTT 客户端
162 110 */
163 111 private void startMqttClient() {
164   - log.info("[startMqttClient][启动 MQTT 客户端]");
165   -
166 112 try {
167   - // 1. 初始化消息处理器
168   - this.upstreamHandler = new IotMqttUpstreamHandler(this);
  113 + // 2.1. 初始化消息处理器
  114 + this.upstreamHandler = new IotEmqxUpstreamHandler(this);
169 115  
170   - // 2. 创建 MQTT 客户端
171   - log.info("[startMqttClient][使用 MQTT 客户端 ID: {}]", emqxProperties.getMqttClientId());
  116 + // 2.2. 创建 MQTT 客户端
172 117 createMqttClient();
173 118  
174   - // 3. 连接 MQTT Broker(同步等待首次连接结果)
  119 + // 2.3. 连接 MQTT Broker(同步等待首次连接结果)
175 120 boolean connected = connectMqttSync();
176 121 if (!connected) {
177 122 throw new RuntimeException("首次连接 MQTT Broker 失败");
178 123 }
179   -
180   - log.info("[startMqttClient][MQTT 客户端启动完成]");
181 124 } catch (Exception e) {
182 125 log.error("[startMqttClient][MQTT 客户端启动失败]", e);
183 126 throw new RuntimeException("MQTT 客户端启动失败", e);
... ... @@ -185,59 +128,89 @@ public class IotMqttUpstreamProtocol {
185 128 }
186 129  
187 130 /**
188   - * 同步连接 MQTT Broker
  131 + * 连接 MQTT Broker
189 132 *
190   - * @return 是否连接成功
  133 + * @param isReconnect 是否为重连
  134 + * @param isSync 是否同步等待连接结果
  135 + * @return 当 isSync 为 true 时返回连接是否成功,否则返回 null
191 136 */
192   - private boolean connectMqttSync() {
  137 + private Boolean connectMqtt(boolean isReconnect, boolean isSync) {
193 138 String host = emqxProperties.getMqttHost();
194 139 Integer port = emqxProperties.getMqttPort();
195   - log.info("[connectMqttSync][开始连接 MQTT Broker, host: {}, port: {}]", host, port);
196 140  
197   - // 使用计数器实现同步等待
198   - java.util.concurrent.CountDownLatch latch = new java.util.concurrent.CountDownLatch(1);
199   - java.util.concurrent.atomic.AtomicBoolean success = new java.util.concurrent.atomic.AtomicBoolean(false);
  141 + // 2.3.1. 如果是重连,则需要重新创建 MQTT 客户端
  142 + if (isReconnect) {
  143 + createMqttClient();
  144 + }
  145 +
  146 + // 2.3.2. 连接 MQTT Broker
  147 + CountDownLatch latch = isSync ? new CountDownLatch(1) : null;
  148 + AtomicBoolean success = isSync
  149 + ? new AtomicBoolean(false)
  150 + : null;
200 151  
201 152 mqttClient.connect(port, host, connectResult -> {
202 153 if (connectResult.succeeded()) {
203   - log.info("[connectMqttSync][MQTT 客户端连接成功, host: {}, port: {}]", host, port);
204   - // 设置处理器
  154 + if (isReconnect) {
  155 + log.info("[connectMqtt][MQTT 客户端重连成功]");
  156 + } else {
  157 + log.info("[connectMqtt][MQTT 客户端连接成功, host: {}, port: {}]", host, port);
  158 + }
  159 + // 设置处理器和订阅主题
205 160 setupMqttHandlers();
206   - // 订阅主题
207 161 subscribeToTopics();
208   - success.set(true);
  162 + if (success != null) {
  163 + success.set(true);
  164 + }
209 165 } else {
210   - log.error("[connectMqttSync][连接 MQTT Broker 失败, host: {}, port: {}]",
211   - host, port, connectResult.cause());
212   - // 首次连接失败,启动重连机制
213   - reconnectWithDelay();
  166 + log.error("[connectMqtt][连接 MQTT Broker 失败, host: {}, port: {}]", host, port, connectResult.cause());
  167 + if (isReconnect) {
  168 + reconnectWithDelay();
  169 + } else {
  170 + log.error("[connectMqtt][首次连接失败,连接终止]");
  171 + }
  172 + }
  173 +
  174 + if (latch != null) {
  175 + latch.countDown();
214 176 }
215   - latch.countDown();
216 177 });
217 178  
218   - try {
219   - // 等待连接结果,最多等待10秒
220   - latch.await(10, java.util.concurrent.TimeUnit.SECONDS);
221   - } catch (InterruptedException e) {
222   - Thread.currentThread().interrupt();
223   - log.error("[connectMqttSync][等待连接结果被中断]", e);
  179 + // 2.3.3. 如果需要同步等待连接结果,则等待
  180 + if (isSync) {
  181 + try {
  182 + latch.await(10, java.util.concurrent.TimeUnit.SECONDS);
  183 + } catch (InterruptedException e) {
  184 + Thread.currentThread().interrupt();
  185 + log.error("[connectMqtt][等待连接结果被中断]", e);
  186 + }
  187 + return success.get();
224 188 }
225 189  
226   - return success.get();
  190 + return null;
  191 + }
  192 +
  193 + /**
  194 + * 同步连接 MQTT Broker
  195 + *
  196 + * @return 是否连接成功
  197 + */
  198 + private boolean connectMqttSync() {
  199 + Boolean result = connectMqtt(false, true);
  200 + return result != null ? result : false;
227 201 }
228 202  
229 203 /**
230 204 * 停止 MQTT 客户端
231 205 */
232 206 private void stopMqttClient() {
233   - // 1. 取消 MQTT 主题订阅
  207 + // 1.1. 取消订阅所有主题
234 208 if (mqttClient != null && mqttClient.isConnected()) {
235 209 List<String> topicList = emqxProperties.getMqttTopics();
236 210 if (CollUtil.isNotEmpty(topicList)) {
237 211 for (String topic : topicList) {
238 212 try {
239 213 mqttClient.unsubscribe(topic);
240   - log.debug("[stopMqttClient][取消订阅主题: {}]", topic);
241 214 } catch (Exception e) {
242 215 log.warn("[stopMqttClient][取消订阅主题({})异常]", topic, e);
243 216 }
... ... @@ -245,11 +218,10 @@ public class IotMqttUpstreamProtocol {
245 218 }
246 219 }
247 220  
248   - // 2. 关闭 MQTT 客户端
  221 + // 1.2. 断开 MQTT 客户端连接
249 222 if (mqttClient != null && mqttClient.isConnected()) {
250 223 try {
251 224 mqttClient.disconnect();
252   - log.info("[stopMqttClient][MQTT 客户端已断开]");
253 225 } catch (Exception e) {
254 226 log.warn("[stopMqttClient][关闭 MQTT 客户端异常]", e);
255 227 }
... ... @@ -257,51 +229,6 @@ public class IotMqttUpstreamProtocol {
257 229 }
258 230  
259 231 /**
260   - * 连接 MQTT Broker 并订阅主题
261   - *
262   - * @param isReconnect 是否为重连
263   - */
264   - private void connectMqtt(boolean isReconnect) {
265   - // 1. 参数校验
266   - String host = emqxProperties.getMqttHost();
267   - Integer port = emqxProperties.getMqttPort();
268   -
269   - if (isReconnect) {
270   - log.info("[connectMqtt][开始重连 MQTT Broker, host: {}, port: {}]", host, port);
271   - // 重连时重新创建客户端实例
272   - createMqttClient();
273   - } else {
274   - log.info("[connectMqtt][开始连接 MQTT Broker, host: {}, port: {}]", host, port);
275   - }
276   -
277   - // 2. 异步连接
278   - mqttClient.connect(port, host, connectResult -> {
279   - if (!connectResult.succeeded()) {
280   - log.error("[connectMqtt][连接 MQTT Broker 失败, host: {}, port: {}, isReconnect: {}]",
281   - host, port, isReconnect, connectResult.cause());
282   -
283   - // 首次连接失败或重连失败时,尝试重连
284   - if (!isReconnect) {
285   - log.warn("[connectMqtt][首次连接失败,将开始重连机制]");
286   - }
287   - reconnectWithDelay();
288   - return;
289   - }
290   -
291   - if (isReconnect) {
292   - log.info("[connectMqtt][MQTT 客户端重连成功, host: {}, port: {}]", host, port);
293   - } else {
294   - log.info("[connectMqtt][MQTT 客户端连接成功, host: {}, port: {}]", host, port);
295   - }
296   -
297   - // 设置处理器
298   - setupMqttHandlers();
299   - // 订阅主题
300   - subscribeToTopics();
301   - });
302   - }
303   -
304   - /**
305 232 * 创建 MQTT 客户端
306 233 */
307 234 private void createMqttClient() {
... ... @@ -317,73 +244,48 @@ public class IotMqttUpstreamProtocol {
317 244 * 设置 MQTT 处理器
318 245 */
319 246 private void setupMqttHandlers() {
320   - // 由于 mqttClient 在 createMqttClient() 方法中已初始化,此处无需检查
321   - // 设置断开重连监听器
  247 + // 1. 设置断开重连监听器
322 248 mqttClient.closeHandler(closeEvent -> {
323   - log.warn("[closeHandler][MQTT 连接已断开, 准备重连]");
324   - reconnectWithDelay();
  249 + if (isRunning) {
  250 + log.warn("[closeHandler][MQTT 连接已断开, 准备重连]");
  251 + reconnectWithDelay();
  252 + }
325 253 });
326 254  
327   - // 设置异常处理器
328   - mqttClient.exceptionHandler(exception -> {
329   - log.error("[exceptionHandler][MQTT 客户端异常]", exception);
330   - });
  255 + // 2. 设置异常处理器
  256 + mqttClient.exceptionHandler(exception -> log.error("[exceptionHandler][MQTT 客户端异常]", exception));
331 257  
332   - // 设置消息处理器
333   - // upstreamHandler 在 startMqttClient() 方法中已初始化,此处无需检查
  258 + // 3. 设置消息处理器
334 259 mqttClient.publishHandler(upstreamHandler::handle);
335   - log.debug("[setupMqttHandlers][MQTT 消息处理器设置完成]");
336 260 }
337 261  
338 262 /**
339 263 * 订阅设备上行消息主题
340 264 */
341 265 private void subscribeToTopics() {
  266 + // 1. 校验 MQTT 客户端是否连接
342 267 List<String> topicList = emqxProperties.getMqttTopics();
343   - if (CollUtil.isEmpty(topicList)) {
344   - log.warn("[subscribeToTopics][订阅主题列表为空, 跳过订阅]");
345   - return;
346   - }
347 268 if (mqttClient == null || !mqttClient.isConnected()) {
348 269 log.warn("[subscribeToTopics][MQTT 客户端未连接, 跳过订阅]");
349 270 return;
350 271 }
351 272  
352 273 int qos = emqxProperties.getMqttQos();
353   - log.info("[subscribeToTopics][开始订阅主题, 共 {} 个, QoS: {}]", topicList.size(), qos);
354   -
355   - // 使用 AtomicInteger 替代数组,线程安全且更简洁
356   - AtomicInteger successCount = new AtomicInteger(0);
357   - AtomicInteger failCount = new AtomicInteger(0);
358 274  
359   - // 构建主题-QoS 映射,批量订阅
  275 + // 2. 构建主题-QoS 映射,批量订阅
360 276 Map<String, Integer> topicQosMap = new HashMap<>();
361 277 for (String topic : topicList) {
362 278 topicQosMap.put(topic, qos);
363 279 }
364 280  
365   - // 批量订阅所有主题
  281 + // 3. 批量订阅所有主题
366 282 mqttClient.subscribe(topicQosMap, subscribeResult -> {
367 283 if (subscribeResult.succeeded()) {
368   - // 批量订阅成功,记录所有主题为成功
369   - int successful = successCount.addAndGet(topicList.size());
370   - log.info("[subscribeToTopics][批量订阅主题成功, 共 {} 个主题, QoS: {}]", successful, qos);
371   - for (String topic : topicList) {
372   - log.debug("[subscribeToTopics][订阅主题成功, topic: {}, qos: {}]", topic, qos);
373   - }
  284 + log.info("[subscribeToTopics][订阅主题成功, 共 {} 个主题]", topicList.size());
374 285 } else {
375   - // 批量订阅失败,记录所有主题为失败
376   - int failed = failCount.addAndGet(topicList.size());
377   - log.error("[subscribeToTopics][批量订阅主题失败, 共 {} 个主题, 原因: {}]",
378   - failed, subscribeResult.cause().getMessage(), subscribeResult.cause());
379   - for (String topic : topicList) {
380   - log.error("[subscribeToTopics][订阅主题失败, topic: {}, qos: {}]", topic, qos);
381   - }
  286 + log.error("[subscribeToTopics][订阅主题失败, 共 {} 个主题, 原因: {}]",
  287 + topicList.size(), subscribeResult.cause().getMessage(), subscribeResult.cause());
382 288 }
383   -
384   - // 记录汇总日志
385   - log.info("[subscribeToTopics][主题订阅完成, 成功: {}, 失败: {}, 总计: {}]",
386   - successCount.get(), failCount.get(), topicList.size());
387 289 });
388 290 }
389 291  
... ... @@ -394,20 +296,16 @@ public class IotMqttUpstreamProtocol {
394 296 long delay = emqxProperties.getReconnectDelayMs();
395 297 vertx.setTimer(delay, timerId -> {
396 298 if (!isRunning) {
397   - log.debug("[reconnectWithDelay][服务已停止, 取消重连]");
398 299 return;
399 300 }
400   - // 检查连接状态,如果已连接则无需重连
401 301 if (mqttClient != null && mqttClient.isConnected()) {
402   - log.debug("[reconnectWithDelay][MQTT 客户端已连接, 无需重连]");
403 302 return;
404 303 }
405   - log.info("[reconnectWithDelay][开始重连 MQTT Broker, 延迟: {} ms]", delay);
  304 + log.info("[reconnectWithDelay][开始重连 MQTT Broker]");
406 305 try {
407   - connectMqtt(true); // 标记为重连
  306 + connectMqtt(true, false);
408 307 } catch (Exception e) {
409 308 log.error("[reconnectWithDelay][重连失败, 将继续尝试]", e);
410   - // 重连失败时,不需要重复调用,因为 connectMqtt(true) 内部已经处理了重连逻辑
411 309 }
412 310 });
413 311 }
... ... @@ -420,7 +318,7 @@ public class IotMqttUpstreamProtocol {
420 318 */
421 319 public void publishMessage(String topic, String payload) {
422 320 if (mqttClient == null || !mqttClient.isConnected()) {
423   - log.warn("[publishMessage][MQTT 客户端未连接, 无法发布消息到 topic({})]", topic);
  321 + log.warn("[publishMessage][MQTT 客户端未连接, 无法发布消息]");
424 322 return;
425 323 }
426 324 MqttQoS qos = MqttQoS.valueOf(emqxProperties.getMqttQos());
... ...
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/mqtt/router/IotMqttHttpAuthHandler.java renamed to yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/emqx/router/IotEmqxAuthEventHandler.java
1   -package cn.iocoder.yudao.module.iot.gateway.protocol.mqtt.router;
  1 +package cn.iocoder.yudao.module.iot.gateway.protocol.emqx.router;
2 2  
3 3 import cn.hutool.core.util.BooleanUtil;
4 4 import cn.hutool.core.util.StrUtil;
... ... @@ -8,21 +8,23 @@ import cn.iocoder.yudao.module.iot.core.biz.IotDeviceCommonApi;
8 8 import cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceAuthReqDTO;
9 9 import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
10 10 import cn.iocoder.yudao.module.iot.core.util.IotDeviceAuthUtils;
11   -import cn.iocoder.yudao.module.iot.gateway.protocol.mqtt.IotMqttUpstreamProtocol;
12 11 import cn.iocoder.yudao.module.iot.gateway.service.device.message.IotDeviceMessageService;
13 12 import io.vertx.core.json.JsonObject;
14 13 import io.vertx.ext.web.RoutingContext;
15 14 import lombok.extern.slf4j.Slf4j;
16 15  
17 16 /**
18   - * IoT 网关 MQTT HTTP 认证处理器
  17 + * IoT 网关 EMQX 认证事件处理器
19 18 * <p>
20   - * 处理 EMQX 的认证请求和事件钩子,提供统一的错误处理和参数校验
  19 + * 为 EMQX 提供 HTTP 接口服务,包括:
  20 + * 1. 设备认证接口 - 对应 EMQX HTTP 认证插件
  21 + * 2. 设备事件处理接口 - 对应 EMQX Webhook 事件通知
  22 + * 提供统一的错误处理和参数校验
21 23 *
22 24 * @author 芋道源码
23 25 */
24 26 @Slf4j
25   -public class IotMqttHttpAuthHandler {
  27 +public class IotEmqxAuthEventHandler {
26 28  
27 29 /**
28 30 * HTTP 成功状态码(EMQX 要求固定使用 200)
... ... @@ -48,14 +50,14 @@ public class IotMqttHttpAuthHandler {
48 50 private static final String EVENT_CLIENT_CONNECTED = "client.connected";
49 51 private static final String EVENT_CLIENT_DISCONNECTED = "client.disconnected";
50 52  
51   - private final IotMqttUpstreamProtocol protocol;
  53 + private final String serverId;
52 54  
53 55 private final IotDeviceMessageService deviceMessageService;
54 56  
55 57 private final IotDeviceCommonApi deviceApi;
56 58  
57   - public IotMqttHttpAuthHandler(IotMqttUpstreamProtocol protocol) {
58   - this.protocol = protocol;
  59 + public IotEmqxAuthEventHandler(String serverId) {
  60 + this.serverId = serverId;
59 61 this.deviceMessageService = SpringUtil.getBean(IotDeviceMessageService.class);
60 62 this.deviceApi = SpringUtil.getBean(IotDeviceCommonApi.class);
61 63 }
... ... @@ -216,7 +218,7 @@ public class IotMqttHttpAuthHandler {
216 218  
217 219 // 3. 发送设备状态消息
218 220 deviceMessageService.sendDeviceMessage(message,
219   - deviceInfo.getProductKey(), deviceInfo.getDeviceName(), protocol.getServerId());
  221 + deviceInfo.getProductKey(), deviceInfo.getDeviceName(), serverId);
220 222 } catch (Exception e) {
221 223 log.error("[handleDeviceStateChange][发送设备状态消息失败: {}]", username, e);
222 224 }
... ...
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/mqtt/router/IotMqttDownstreamHandler.java renamed to yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/emqx/router/IotEmqxDownstreamHandler.java
1   -package cn.iocoder.yudao.module.iot.gateway.protocol.mqtt.router;
  1 +package cn.iocoder.yudao.module.iot.gateway.protocol.emqx.router;
2 2  
3 3 import cn.hutool.core.util.StrUtil;
4 4 import cn.hutool.extra.spring.SpringUtil;
... ... @@ -6,29 +6,29 @@ import cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceRespDTO;
6 6 import cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum;
7 7 import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
8 8 import cn.iocoder.yudao.module.iot.core.util.IotDeviceMessageUtils;
9   -import cn.iocoder.yudao.module.iot.gateway.protocol.mqtt.IotMqttUpstreamProtocol;
  9 +import cn.iocoder.yudao.module.iot.gateway.protocol.emqx.IotEmqxUpstreamProtocol;
10 10 import cn.iocoder.yudao.module.iot.gateway.service.device.IotDeviceService;
11 11 import cn.iocoder.yudao.module.iot.gateway.service.device.message.IotDeviceMessageService;
12 12 import cn.iocoder.yudao.module.iot.gateway.util.IotMqttTopicUtils;
13 13 import lombok.extern.slf4j.Slf4j;
14 14  
15 15 /**
16   - * IoT 网关 MQTT 下行消息处理器
  16 + * IoT 网关 EMQX 下行消息处理器
17 17 * <p>
18 18 * 从消息总线接收到下行消息,然后发布到 MQTT Broker,从而被设备所接收
19 19 *
20 20 * @author 芋道源码
21 21 */
22 22 @Slf4j
23   -public class IotMqttDownstreamHandler {
  23 +public class IotEmqxDownstreamHandler {
24 24  
25   - private final IotMqttUpstreamProtocol protocol;
  25 + private final IotEmqxUpstreamProtocol protocol;
26 26  
27 27 private final IotDeviceService deviceService;
28 28  
29 29 private final IotDeviceMessageService deviceMessageService;
30 30  
31   - public IotMqttDownstreamHandler(IotMqttUpstreamProtocol protocol) {
  31 + public IotEmqxDownstreamHandler(IotEmqxUpstreamProtocol protocol) {
32 32 this.protocol = protocol;
33 33 this.deviceService = SpringUtil.getBean(IotDeviceService.class);
34 34 this.deviceMessageService = SpringUtil.getBean(IotDeviceMessageService.class);
... ...
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/mqtt/router/IotMqttUpstreamHandler.java renamed to yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/emqx/router/IotEmqxUpstreamHandler.java
1   -package cn.iocoder.yudao.module.iot.gateway.protocol.mqtt.router;
  1 +package cn.iocoder.yudao.module.iot.gateway.protocol.emqx.router;
2 2  
3 3 import cn.hutool.core.util.StrUtil;
4 4 import cn.hutool.extra.spring.SpringUtil;
5 5 import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
6   -import cn.iocoder.yudao.module.iot.gateway.protocol.mqtt.IotMqttUpstreamProtocol;
  6 +import cn.iocoder.yudao.module.iot.gateway.protocol.emqx.IotEmqxUpstreamProtocol;
7 7 import cn.iocoder.yudao.module.iot.gateway.service.device.message.IotDeviceMessageService;
8 8 import io.vertx.mqtt.messages.MqttPublishMessage;
9 9 import lombok.extern.slf4j.Slf4j;
10 10  
11 11 /**
12   - * IoT 网关 MQTT 上行消息处理器
  12 + * IoT 网关 EMQX 上行消息处理器
13 13 *
14 14 * @author 芋道源码
15 15 */
16 16 @Slf4j
17   -public class IotMqttUpstreamHandler {
  17 +public class IotEmqxUpstreamHandler {
18 18  
19 19 private final IotDeviceMessageService deviceMessageService;
20 20  
21 21 private final String serverId;
22 22  
23   - public IotMqttUpstreamHandler(IotMqttUpstreamProtocol protocol) {
  23 + public IotEmqxUpstreamHandler(IotEmqxUpstreamProtocol protocol) {
24 24 this.deviceMessageService = SpringUtil.getBean(IotDeviceMessageService.class);
25 25 this.serverId = protocol.getServerId();
26 26 }
... ...