Commit 7e49c901562889b22cbe85e153ad0cf23d51d9bd

Authored by YunaiV
1 parent f1368d9e

review:【IoT 物联网】MqTT 协议

yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/enums/IotDeviceMessageMethodEnum.java
... ... @@ -23,12 +23,13 @@ public enum IotDeviceMessageMethodEnum implements ArrayValuable<String> {
23 23 STATE_OFFLINE("thing.state.offline", true),
24 24  
25 25 // ========== 设备属性 ==========
26   - // 可参考
27   - // https://help.aliyun.com/zh/iot/user-guide/device-properties-events-and-services
  26 + // 可参考:https://help.aliyun.com/zh/iot/user-guide/device-properties-events-and-services
  27 +
28 28 PROPERTY_POST("thing.property.post", true),
29 29 PROPERTY_SET("thing.property.set", false),
30 30  
31 31 // ========== 设备事件 ==========
  32 +
32 33 EVENT_POST("thing.event.post", true),
33 34  
34 35 ;
... ...
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/config/IotGatewayProperties.java
... ... @@ -105,6 +105,7 @@ public class IotGatewayProperties {
105 105 @NotNull(message = "是否开启不能为空")
106 106 private Boolean enabled;
107 107  
  108 + // TODO @haohao:是不是改成 httpPort?不只认证,目前看。
108 109 /**
109 110 * HTTP 认证端口(默认:8090)
110 111 */
... ... @@ -115,42 +116,35 @@ public class IotGatewayProperties {
115 116 */
116 117 @NotEmpty(message = "MQTT 服务器地址不能为空")
117 118 private String mqttHost;
118   -
119 119 /**
120 120 * MQTT 服务器端口(默认:1883)
121 121 */
122 122 @NotNull(message = "MQTT 服务器端口不能为空")
123 123 private Integer mqttPort = 1883;
124   -
125 124 /**
126 125 * MQTT 用户名
127 126 */
128 127 @NotEmpty(message = "MQTT 用户名不能为空")
129 128 private String mqttUsername;
130   -
131 129 /**
132 130 * MQTT 密码
133 131 */
134 132 @NotEmpty(message = "MQTT 密码不能为空")
135 133 private String mqttPassword;
136   -
137 134 /**
138 135 * MQTT 客户端的 SSL 开关
139 136 */
140 137 @NotNull(message = "MQTT 是否开启 SSL 不能为空")
141 138 private Boolean mqttSsl = false;
142   -
143 139 /**
144 140 * MQTT 客户端 ID(如果为空,系统将自动生成)
145 141 */
146 142 private String mqttClientId;
147   -
148 143 /**
149 144 * MQTT 订阅的主题
150 145 */
151 146 @NotEmpty(message = "MQTT 主题不能为空")
152   - private List<String> mqttTopics;
153   -
  147 + private List<@NotEmpty(message = "MQTT 主题不能为空") String> mqttTopics;
154 148 /**
155 149 * 默认 QoS 级别
156 150 * <p>
... ... @@ -164,12 +158,12 @@ public class IotGatewayProperties {
164 158 * 连接超时时间(秒)
165 159 */
166 160 private Integer connectTimeoutSeconds = 10;
167   -
168 161 /**
169 162 * 重连延迟时间(毫秒)
170 163 */
171 164 private Long reconnectDelayMs = 5000L;
172 165  
  166 + // TODO @haohao:貌似可以通过配置文件 + el 表达式;尽量还是配置文件;
173 167 /**
174 168 * 获取 MQTT 客户端 ID,如果未配置则自动生成
175 169 *
... ...
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/mqtt/IotMqttUpstreamProtocol.java
... ... @@ -35,6 +35,11 @@ public class IotMqttUpstreamProtocol {
35 35  
36 36 private final IotGatewayProperties.EmqxProperties emqxProperties;
37 37  
  38 + /**
  39 + * 服务运行状态标志
  40 + */
  41 + private volatile boolean isRunning = false;
  42 +
38 43 private Vertx vertx;
39 44  
40 45 @Getter
... ... @@ -47,11 +52,6 @@ public class IotMqttUpstreamProtocol {
47 52 // HTTP 认证服务相关
48 53 private HttpServer httpAuthServer;
49 54  
50   - /**
51   - * 服务运行状态标志
52   - */
53   - private volatile boolean isRunning = false;
54   -
55 55 public IotMqttUpstreamProtocol(IotGatewayProperties.EmqxProperties emqxProperties) {
56 56 this.emqxProperties = emqxProperties;
57 57 this.serverId = IotDeviceMessageUtils.generateServerId(emqxProperties.getMqttPort());
... ... @@ -66,13 +66,12 @@ public class IotMqttUpstreamProtocol {
66 66 log.info("[start][启动 MQTT 协议服务]");
67 67  
68 68 try {
69   - // 1. 创建共享的 Vertx 实例
70 69 this.vertx = Vertx.vertx();
71 70  
72   - // 2. 启动 HTTP 认证服务
  71 + // 1. 启动 HTTP 认证服务
73 72 startHttpAuthServer();
74 73  
75   - // 3. 启动 MQTT 客户端
  74 + // 2. 启动 MQTT 客户端
76 75 startMqttClient();
77 76  
78 77 isRunning = true;
... ... @@ -119,16 +118,15 @@ public class IotMqttUpstreamProtocol {
119 118 private void startHttpAuthServer() {
120 119 log.info("[startHttpAuthServer][启动 HTTP 认证服务]");
121 120  
122   - // 创建路由
  121 + // 1.1 创建路由
123 122 Router router = Router.router(vertx);
124 123 router.route().handler(BodyHandler.create());
125   -
126   - // 创建认证处理器
  124 + // 1.2 创建认证处理器
127 125 IotMqttHttpAuthHandler authHandler = new IotMqttHttpAuthHandler(this);
128 126 router.post(IotMqttTopicUtils.MQTT_AUTH_PATH).handler(authHandler::handleAuth);
129 127 router.post(IotMqttTopicUtils.MQTT_EVENT_PATH).handler(authHandler::handleEvent);
130 128  
131   - // 启动 HTTP 服务器
  129 + // 2. 启动 HTTP 服务器
132 130 int authPort = emqxProperties.getHttpAuthPort();
133 131 try {
134 132 httpAuthServer = vertx.createHttpServer()
... ... @@ -172,7 +170,7 @@ public class IotMqttUpstreamProtocol {
172 170 createMqttClient();
173 171  
174 172 // 3. 连接 MQTT Broker(异步连接,不会抛出异常)
175   - connectMqtt();
  173 + connectMqtt(false);
176 174  
177 175 log.info("[startMqttClient][MQTT 客户端启动完成,正在异步连接中...]");
178 176 } catch (Exception e) {
... ... @@ -213,13 +211,6 @@ public class IotMqttUpstreamProtocol {
213 211  
214 212 /**
215 213 * 连接 MQTT Broker 并订阅主题
216   - */
217   - private void connectMqtt() {
218   - connectMqtt(false);
219   - }
220   -
221   - /**
222   - * 连接 MQTT Broker 并订阅主题
223 214 *
224 215 * @param isReconnect 是否为重连
225 216 */
... ... @@ -227,6 +218,7 @@ public class IotMqttUpstreamProtocol {
227 218 // 1. 参数校验
228 219 String host = emqxProperties.getMqttHost();
229 220 Integer port = emqxProperties.getMqttPort();
  221 + // TODO @haohao:这些参数校验,交给 validator;
230 222 if (StrUtil.isBlank(host)) {
231 223 log.error("[connectMqtt][MQTT Host 为空, 无法连接]");
232 224 throw new IllegalArgumentException("MQTT Host 不能为空");
... ... @@ -246,6 +238,7 @@ public class IotMqttUpstreamProtocol {
246 238  
247 239 // 2. 异步连接
248 240 mqttClient.connect(port, host, connectResult -> {
  241 + // TODO @haohao:if return,减少括号哈;
249 242 if (connectResult.succeeded()) {
250 243 if (isReconnect) {
251 244 log.info("[connectMqtt][MQTT 客户端重连成功, host: {}, port: {}]", host, port);
... ... @@ -253,16 +246,15 @@ public class IotMqttUpstreamProtocol {
253 246 log.info("[connectMqtt][MQTT 客户端连接成功, host: {}, port: {}]", host, port);
254 247 }
255 248  
256   - // 3. 设置处理器
  249 + // 设置处理器
257 250 setupMqttHandlers();
258   -
259   - // 4. 订阅主题
  251 + // 订阅主题
260 252 subscribeToTopics();
261   -
262 253 } else {
263 254 log.error("[connectMqtt][连接 MQTT Broker 失败, host: {}, port: {}, isReconnect: {}]",
264 255 host, port, isReconnect, connectResult.cause());
265 256  
  257 + // TODO @haohao:体感上,是不是首次必须连接成功?类似 mysql;首次要连接上,然后后续可以重连;
266 258 if (!isReconnect) {
267 259 // 首次连接失败时,也要尝试重连
268 260 log.warn("[connectMqtt][首次连接失败,将开始重连机制]");
... ... @@ -279,14 +271,11 @@ public class IotMqttUpstreamProtocol {
279 271 * 创建 MQTT 客户端
280 272 */
281 273 private void createMqttClient() {
282   - log.debug("[createMqttClient][创建 MQTT 客户端, clientId: {}]", emqxProperties.getMqttClientId());
283   -
284 274 MqttClientOptions options = new MqttClientOptions()
285 275 .setClientId(emqxProperties.getMqttClientId())
286 276 .setUsername(emqxProperties.getMqttUsername())
287 277 .setPassword(emqxProperties.getMqttPassword())
288 278 .setSsl(emqxProperties.getMqttSsl());
289   -
290 279 this.mqttClient = MqttClient.create(vertx, options);
291 280 }
292 281  
... ... @@ -294,6 +283,7 @@ public class IotMqttUpstreamProtocol {
294 283 * 设置 MQTT 处理器
295 284 */
296 285 private void setupMqttHandlers() {
  286 + // TODO @haohao:mqttClient 一定非空;
297 287 if (mqttClient == null) {
298 288 log.warn("[setupMqttHandlers][MQTT 客户端为空,跳过处理器设置]");
299 289 return;
... ... @@ -311,6 +301,7 @@ public class IotMqttUpstreamProtocol {
311 301 });
312 302  
313 303 // 设置消息处理器
  304 + // TODO @haohao:upstreamHandler 一定非空;
314 305 if (upstreamHandler != null) {
315 306 mqttClient.publishHandler(upstreamHandler::handle);
316 307 log.debug("[setupMqttHandlers][MQTT 消息处理器设置完成]");
... ... @@ -328,7 +319,6 @@ public class IotMqttUpstreamProtocol {
328 319 log.warn("[subscribeToTopics][订阅主题列表为空, 跳过订阅]");
329 320 return;
330 321 }
331   -
332 322 if (mqttClient == null || !mqttClient.isConnected()) {
333 323 log.warn("[subscribeToTopics][MQTT 客户端未连接, 跳过订阅]");
334 324 return;
... ... @@ -337,10 +327,12 @@ public class IotMqttUpstreamProtocol {
337 327 int qos = emqxProperties.getMqttQos();
338 328 log.info("[subscribeToTopics][开始订阅主题, 共 {} 个, QoS: {}]", topicList.size(), qos);
339 329  
  330 + // TODO @haohao:使用 atomicinteger 会更合适;
340 331 int[] successCount = { 0 }; // 使用数组以便在 lambda 中修改
341 332 int[] failCount = { 0 };
342 333  
343 334 for (String topic : topicList) {
  335 + // TODO @haohao:MqttClient subscribe(Map<String, Integer> topics, 是不是更简洁哈;
344 336 mqttClient.subscribe(topic, qos, subscribeResult -> {
345 337 if (subscribeResult.succeeded()) {
346 338 successCount[0]++;
... ...
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/mqtt/router/IotMqttDownstreamHandler.java
... ... @@ -15,7 +15,7 @@ import lombok.extern.slf4j.Slf4j;
15 15 /**
16 16 * IoT 网关 MQTT 下行消息处理器
17 17 * <p>
18   - * 从消息总线接收到下行消息,然后发布到 MQTT Broker
  18 + * 从消息总线接收到下行消息,然后发布到 MQTT Broker,从而被设备所接收
19 19 *
20 20 * @author 芋道源码
21 21 */
... ... @@ -23,7 +23,9 @@ import lombok.extern.slf4j.Slf4j;
23 23 public class IotMqttDownstreamHandler {
24 24  
25 25 private final IotMqttUpstreamProtocol protocol;
  26 +
26 27 private final IotDeviceService deviceService;
  28 +
27 29 private final IotDeviceMessageService deviceMessageService;
28 30  
29 31 public IotMqttDownstreamHandler(IotMqttUpstreamProtocol protocol) {
... ... @@ -41,25 +43,24 @@ public class IotMqttDownstreamHandler {
41 43 // 1. 获取设备信息(使用缓存)
42 44 IotDeviceRespDTO deviceInfo = deviceService.getDeviceFromCache(message.getDeviceId());
43 45 if (deviceInfo == null) {
44   - log.warn("[handle][设备信息不存在, deviceId: {}]", message.getDeviceId());
  46 + log.error("[handle][设备信息({})不存在]", message.getDeviceId());
45 47 return;
46 48 }
47 49  
48   - // 2. 根据方法构建主题
  50 + // 2.1 根据方法构建主题
49 51 String topic = buildTopicByMethod(message.getMethod(), deviceInfo.getProductKey(), deviceInfo.getDeviceName());
50 52 if (StrUtil.isBlank(topic)) {
51 53 log.warn("[handle][未知的消息方法: {}]", message.getMethod());
52 54 return;
53 55 }
54   -
55   - // 3. 构建载荷
  56 + // 2.2 构建载荷
  57 + // TODO @haohao:这里是不是 encode 就可以发拉?因为本身就 json 化了。
56 58 JSONObject payload = buildDownstreamPayload(message);
57   -
58   - // 4. 发布消息
  59 + // 2.3 发布消息
59 60 protocol.publishMessage(topic, payload.toString());
60   - log.debug("[handle][发布下行消息成功, method: {}, topic: {}]", message.getMethod(), topic);
61 61 }
62 62  
  63 + // TODO @haohao:这个是不是也可以计算;IotDeviceMessageUtils 的 isReplyMessage;这样就直接生成了;
63 64 /**
64 65 * 根据方法构建主题
65 66 *
... ...
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/mqtt/router/IotMqttHttpAuthHandler.java
... ... @@ -35,12 +35,10 @@ public class IotMqttHttpAuthHandler {
35 35 * 认证允许结果
36 36 */
37 37 private static final String RESULT_ALLOW = "allow";
38   -
39 38 /**
40 39 * 认证拒绝结果
41 40 */
42 41 private static final String RESULT_DENY = "deny";
43   -
44 42 /**
45 43 * 认证忽略结果
46 44 */
... ... @@ -53,14 +51,11 @@ public class IotMqttHttpAuthHandler {
53 51 private static final String EVENT_CLIENT_DISCONNECTED = "client.disconnected";
54 52  
55 53 private final IotMqttUpstreamProtocol protocol;
  54 +
56 55 private final IotDeviceMessageService deviceMessageService;
  56 +
57 57 private final IotDeviceCommonApi deviceApi;
58 58  
59   - /**
60   - * 构造器
61   - *
62   - * @param protocol MQTT 协议实例
63   - */
64 59 public IotMqttHttpAuthHandler(IotMqttUpstreamProtocol protocol) {
65 60 this.protocol = protocol;
66 61 this.deviceMessageService = SpringUtil.getBean(IotDeviceMessageService.class);
... ... @@ -72,19 +67,15 @@ public class IotMqttHttpAuthHandler {
72 67 */
73 68 public void handleAuth(RoutingContext context) {
74 69 try {
75   - // 解析请求体
  70 + // 参数校验
76 71 JsonObject body = parseRequestBody(context);
77 72 if (body == null) {
78 73 return;
79 74 }
80   -
81 75 String clientId = body.getString("clientid");
82 76 String username = body.getString("username");
83 77 String password = body.getString("password");
84   -
85 78 log.debug("[handleAuth][设备认证请求: clientId={}, username={}]", clientId, username);
86   -
87   - // 参数校验
88 79 if (StrUtil.hasEmpty(clientId, username, password)) {
89 80 log.info("[handleAuth][认证参数不完整: clientId={}, username={}]", clientId, username);
90 81 sendAuthResponse(context, RESULT_DENY, false, "认证参数不完整");
... ... @@ -94,13 +85,13 @@ public class IotMqttHttpAuthHandler {
94 85 // 执行设备认证
95 86 boolean authResult = performDeviceAuth(clientId, username, password);
96 87 if (authResult) {
  88 + // TODO @haohao:是不是两条 info,直接打认证结果:authResult
97 89 log.info("[handleAuth][设备认证成功: {}]", username);
98 90 sendAuthResponse(context, RESULT_ALLOW, false, null);
99 91 } else {
100 92 log.info("[handleAuth][设备认证失败: {}]", username);
101 93 sendAuthResponse(context, RESULT_DENY, false, DEVICE_AUTH_FAIL.getMsg());
102 94 }
103   -
104 95 } catch (Exception e) {
105 96 log.error("[handleAuth][设备认证异常]", e);
106 97 sendAuthResponse(context, RESULT_IGNORE, false, "认证服务异常");
... ... @@ -119,10 +110,8 @@ public class IotMqttHttpAuthHandler {
119 110 if (body == null) {
120 111 return;
121 112 }
122   -
123 113 String event = body.getString("event");
124 114 String username = body.getString("username");
125   -
126 115 log.debug("[handleEvent][收到事件: {} - {}]", event, username);
127 116  
128 117 // 根据事件类型进行分发处理
... ... @@ -138,12 +127,12 @@ public class IotMqttHttpAuthHandler {
138 127 break;
139 128 }
140 129  
141   - // EMQX Webhook 只需要200状态码,无需响应体
  130 + // EMQX Webhook 只需要 200 状态码,无需响应体
142 131 context.response().setStatusCode(SUCCESS_STATUS_CODE).end();
143   -
144 132 } catch (Exception e) {
  133 + // TODO @haohao:body 可以打印出来
145 134 log.error("[handleEvent][事件处理失败]", e);
146   - // 即使处理失败,也返回200避免EMQX重试
  135 + // 即使处理失败,也返回 200 避免EMQX重试
147 136 context.response().setStatusCode(SUCCESS_STATUS_CODE).end();
148 137 }
149 138 }
... ... @@ -163,7 +152,6 @@ public class IotMqttHttpAuthHandler {
163 152 private void handleClientDisconnected(JsonObject body) {
164 153 String username = body.getString("username");
165 154 String reason = body.getString("reason");
166   -
167 155 log.info("[handleClientDisconnected][设备下线: {} ({})]", username, reason);
168 156 handleDeviceStateChange(username, false);
169 157 }
... ... @@ -184,6 +172,7 @@ public class IotMqttHttpAuthHandler {
184 172 }
185 173 return body;
186 174 } catch (Exception e) {
  175 + // TODO @haohao:最好把 body 打印出来;
187 176 log.error("[parseRequestBody][解析请求体失败]", e);
188 177 sendAuthResponse(context, RESULT_IGNORE, false, "请求体格式错误");
189 178 return null;
... ... @@ -201,10 +190,7 @@ public class IotMqttHttpAuthHandler {
201 190 private boolean performDeviceAuth(String clientId, String username, String password) {
202 191 try {
203 192 CommonResult<Boolean> result = deviceApi.authDevice(new IotDeviceAuthReqDTO()
204   - .setClientId(clientId)
205   - .setUsername(username)
206   - .setPassword(password));
207   -
  193 + .setClientId(clientId).setUsername(username).setPassword(password));
208 194 result.checkError();
209 195 return BooleanUtil.isTrue(result.getData());
210 196 } catch (Exception e) {
... ... @@ -220,11 +206,10 @@ public class IotMqttHttpAuthHandler {
220 206 * @param online 是否在线
221 207 */
222 208 private void handleDeviceStateChange(String username, boolean online) {
  209 + // 解析设备信息
223 210 if (StrUtil.isEmpty(username) || "undefined".equals(username)) {
224 211 return;
225 212 }
226   -
227   - // 解析设备信息
228 213 IotDeviceAuthUtils.DeviceInfo deviceInfo = IotDeviceAuthUtils.parseUsername(username);
229 214 if (deviceInfo == null) {
230 215 log.debug("[handleDeviceStateChange][跳过非设备连接: {}]", username);
... ... @@ -232,6 +217,7 @@ public class IotMqttHttpAuthHandler {
232 217 }
233 218  
234 219 try {
  220 + // TODO @haohao:serverId 获取非空,可以忽略掉;
235 221 String serverId = protocol.getServerId();
236 222 if (StrUtil.isEmpty(serverId)) {
237 223 log.error("[handleDeviceStateChange][获取服务器ID失败]");
... ... @@ -241,15 +227,14 @@ public class IotMqttHttpAuthHandler {
241 227 // 构建设备状态消息
242 228 IotDeviceMessage message = online ? IotDeviceMessage.buildStateOnline()
243 229 : IotDeviceMessage.buildStateOffline();
244   -
245 230 // 发送消息到消息总线
246 231 deviceMessageService.sendDeviceMessage(message,
247 232 deviceInfo.getProductKey(), deviceInfo.getDeviceName(), serverId);
248 233  
  234 + // TODO @haohao:online 不用翻译
249 235 log.info("[handleDeviceStateChange][设备状态更新: {}/{} -> {}]",
250 236 deviceInfo.getProductKey(), deviceInfo.getDeviceName(),
251 237 online ? "在线" : "离线");
252   -
253 238 } catch (Exception e) {
254 239 log.error("[handleDeviceStateChange][发送设备状态消息失败: {}]", username, e);
255 240 }
... ... @@ -277,10 +262,6 @@ public class IotMqttHttpAuthHandler {
277 262 // response.put("expire_at", System.currentTimeMillis() / 1000 + 3600);
278 263  
279 264 // 记录详细的响应日志(message仅用于日志,不返回给EMQX)
280   - if (StrUtil.isNotEmpty(message)) {
281   - log.debug("[sendAuthResponse][响应详情: result={}, message={}]", result, message);
282   - }
283   -
284 265 context.response()
285 266 .setStatusCode(SUCCESS_STATUS_CODE)
286 267 .putHeader("Content-Type", "application/json; charset=utf-8")
... ...
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/mqtt/router/IotMqttUpstreamHandler.java
... ... @@ -20,6 +20,7 @@ import java.util.Arrays;
20 20 public class IotMqttUpstreamHandler {
21 21  
22 22 private final IotDeviceMessageService deviceMessageService;
  23 +
23 24 private final String serverId;
24 25  
25 26 public IotMqttUpstreamHandler(IotMqttUpstreamProtocol protocol) {
... ... @@ -30,56 +31,50 @@ public class IotMqttUpstreamHandler {
30 31 /**
31 32 * 处理 MQTT 发布消息
32 33 */
33   - public void handle(MqttPublishMessage message) {
34   - String topic = message.topicName();
35   - byte[] payload = message.payload().getBytes();
36   -
37   - log.debug("[handle][收到 MQTT 消息, topic: {}]", topic);
38   -
  34 + public void handle(MqttPublishMessage mqttMessage) {
  35 + String topic = mqttMessage.topicName();
  36 + byte[] payload = mqttMessage.payload().getBytes();
39 37 try {
40 38 // 1. 前置校验
41 39 if (StrUtil.isBlank(topic)) {
42 40 log.warn("[handle][主题为空, 忽略消息]");
43 41 return;
44 42 }
45   - // 注意:payload 可以为空
46 43  
47   - // 2. 识别并验证消息类型
  44 + // 2.1 识别并验证消息类型
48 45 String messageType = getMessageType(topic);
  46 + // TODO @haohao:可以使用 hutool 的,它的字符串拼接更简单;
49 47 Assert.notNull(messageType, String.format("未知的消息类型, topic(%s)", topic));
50   - log.debug("[handle][接收到上行消息({}), topic: {}]", messageType, topic);
51   -
52   - // 3. 解析主题,获取 productKey 和 deviceName
  48 + // 2.2 解析主题,获取 productKey 和 deviceName
  49 + // TODO @haohao:体感 getMessageType 和下面,都 split;是不是一次就 ok 拉;1)split 掉;2)2、3 位置是 productKey、deviceName;3)4 开始还是 method
53 50 String[] topicParts = topic.split("/");
54 51 if (topicParts.length < 4) {
55   - log.warn("[handle][主题格式不正确,无法解析 productKey 和 deviceName][topic: {}]", topic);
  52 + log.warn("[handle][topic({}) 格式不正确,无法解析 productKey 和 deviceName]", topic);
56 53 return;
57 54 }
58 55 String productKey = topicParts[2];
59 56 String deviceName = topicParts[3];
  57 + // TODO @haohao:是不是要判断,部分为空,就不行呀;
60 58 if (StrUtil.isAllBlank(productKey, deviceName)) {
61   - log.warn("[handle][主题中 productKey 或 deviceName 为空][topic: {}]", topic);
  59 + log.warn("[handle][topic({}) 格式不正确,productKey 和 deviceName 部分为空]", topic);
62 60 return;
63 61 }
64 62  
65   - // 4. 解码消息
66   - IotDeviceMessage deviceMessage = deviceMessageService.decodeDeviceMessage(
67   - payload, productKey, deviceName);
68   - if (deviceMessage == null) {
69   - log.warn("[handle][消息解码失败][topic: {}]", topic);
  63 + // 3. 解码消息
  64 + IotDeviceMessage message = deviceMessageService.decodeDeviceMessage(payload, productKey, deviceName);
  65 + if (message == null) {
  66 + log.warn("[handle][topic({}) payload({}) 消息解码失败", topic, new String(payload));
70 67 return;
71 68 }
72 69  
73   - // 5. 发送消息到队列
74   - deviceMessageService.sendDeviceMessage(deviceMessage, productKey, deviceName, serverId);
75   -
76   - // 6. 记录成功日志
77   - log.debug("[handle][处理上行消息({})成功, topic: {}]", messageType, topic);
  70 + // 4. 发送消息到队列
  71 + deviceMessageService.sendDeviceMessage(message, productKey, deviceName, serverId);
78 72 } catch (Exception e) {
79   - log.error("[handle][处理 MQTT 消息失败][topic: {}][payload: {}]", topic, new String(payload), e);
  73 + log.error("[handle][topic({}) payload({}) 处理异常]", topic, new String(payload), e);
80 74 }
81 75 }
82 76  
  77 + // TODO @haohao:是不是 getMethodFromTopic?
83 78 /**
84 79 * 从主题中,获得消息类型
85 80 *
... ... @@ -89,9 +84,9 @@ public class IotMqttUpstreamHandler {
89 84 private String getMessageType(String topic) {
90 85 String[] topicParts = topic.split("/");
91 86 // 约定:topic 第 4 个部分开始为消息类型
92   - // 例如:/sys/{productKey}/{deviceName}/thing/property/post ->
93   - // thing/property/post
  87 + // 例如:/sys/{productKey}/{deviceName}/thing/property/post -> thing/property/post
94 88 if (topicParts.length > 4) {
  89 + // TODO @haohao:是不是 subString 前 3 个,性能更好;
95 90 return String.join("/", Arrays.copyOfRange(topicParts, 4, topicParts.length));
96 91 }
97 92 return topicParts[topicParts.length - 1];
... ...