Skip to content

Commit a3eee1d

Browse files
authored
fix(网络组件): 修复MQTT连接握手期被误判失活导致下行全部失败 (#786)
根因: VertxMqttConnection.isAlive() 以 endpoint.isConnected() 判活, 而 vertx 只在发出 CONNACK 时才置位 isConnected,导致连接在握手期 (会话注册阶段)恒被误判为失活,引发两类事故: 1. MqttConnectionSession.registerConnection 的 isAlive 守卫把每个新连接 静默丢弃,会话恒空 -> 上行正常但全部下行报"设备连接已断开"; 2. 若仅删守卫让连接入会话,会话管理器注册流程 (doRegister -> getClientAddress -> takeConnection)会把握手中的连接 当死连接 disconnect -> 设备一上线即被断开。 修复(同一契约"isAlive 全生命周期真实"的三个必要组成): - isAlive(): accept 前以 closed 标志判活,accept 后维持原语义 (vertx connack 在 synchronized 内同步置位 isConnected,语义逐字节一致) - accept(): CONNACK 发出后再置位 accepted,保证 accepted=>isConnected 恒成立,两种判活依据切换零窗口 - 构造器提前挂接 close/disconnect 回调(vertx 在 CONNECT 处理时即已 接通 socket closeHandler,允许 accept 前注册),closed 标志从创建起权威 - registerConnection 移除 isAlive 准入守卫,失活连接由 takeConnection/onClose 兜底清理 测试: 新增 MqttConnectionSessionTest + VertxMqttConnectionTest 共 10 例, 含真实 VertxMqttConnection 端到端复现注册期杀链(去修复即红)。 全仓库离线编译通过。
1 parent 8d92d00 commit a3eee1d

4 files changed

Lines changed: 539 additions & 5 deletions

File tree

jetlinks-components/network-component/mqtt-component/src/main/java/org/jetlinks/community/network/mqtt/gateway/device/session/MqttConnectionSession.java

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -98,9 +98,10 @@ public void registerConnection(MqttConnection connection) {
9898
connection.reject(MqttConnectReturnCode.CONNECTION_REFUSED_SERVER_UNAVAILABLE);
9999
return;
100100
}
101-
if (!connection.isAlive()) {
102-
return;
103-
}
101+
//不能在此以 isAlive() 做准入判断:注册发生在 CONNACK(accept)之前,
102+
//该守卫会把握手中的新连接静默丢弃,造成上行正常而全部下行报"设备连接已断开"。
103+
//握手期连接的存活语义由 VertxMqttConnection.isAlive()(closed 标志)保证,
104+
//失活连接由 takeConnection/onClose 兜底清理。
104105
this.add(connection);
105106
connectTime = System.currentTimeMillis();
106107
connection.onClose(this);

jetlinks-components/network-component/mqtt-component/src/main/java/org/jetlinks/community/network/mqtt/server/vertx/VertxMqttConnection.java

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,16 @@ public String getPassword() {
8383
public VertxMqttConnection(MqttEndpoint endpoint) {
8484
this.endpoint = endpoint;
8585
this.keepAliveTimeoutMs = (endpoint.keepAliveTimeSeconds() + 10) * 1000L;
86+
//accept(CONNACK)之前 isAlive() 依赖 closed 标志(见 isAlive 注释),提前挂接关闭回调,
87+
//保证握手期间对端断开也能立即感知。init() 在 accept 后重复注册为 vertx 替换语义,无副作用。
88+
try {
89+
this.endpoint
90+
.closeHandler(ignore -> this.complete())
91+
.disconnectHandler(ignore -> this.complete());
92+
} catch (Throwable e) {
93+
//endpoint 在构造时已被关闭:直接标记失活
94+
this.closed = true;
95+
}
8696
}
8797

8898
private final Consumer<MqttConnection> defaultListener = mqttConnection -> {
@@ -145,11 +155,13 @@ public MqttConnection accept() {
145155
return this;
146156
}
147157
log.debug("mqtt client [{}] connected", getClientId());
148-
accepted = true;
149158
try {
150159
if (!endpoint.isConnected()) {
151160
endpoint.accept();
152161
}
162+
//CONNACK 发出后再置位:保证 accepted=true 时 endpoint.isConnected() 必为 true,
163+
//isAlive() 在 closed 标志与 isConnected 两种依据间切换时不存在瞬时失活窗口
164+
accepted = true;
153165
} catch (Exception e) {
154166
close().subscribe();
155167
log.warn(e.getMessage(), e);
@@ -343,7 +355,12 @@ public void disconnect() {
343355

344356
@Override
345357
public boolean isAlive() {
346-
return endpoint.isConnected() && (keepAliveTimeoutMs < 0 || ((System.currentTimeMillis() - lastPingTime) < keepAliveTimeoutMs));
358+
//CONNACK(accept)之前 vertx MqttEndpoint.isConnected() 恒为 false,不能作为握手阶段的存活依据:
359+
//会话注册发生在 accept 之前,注册过程中会话管理器会触发 getClientAddress -> takeConnection,
360+
//若此阶段误报失活,新连接会被 takeConnection 当作死连接 disconnect,导致设备一上线即被断开。
361+
//因此 accept 前以 closed 标志判活(构造器已提前挂接关闭回调),accept 后维持原语义。
362+
return (accepted ? endpoint.isConnected() : !closed)
363+
&& (keepAliveTimeoutMs < 0 || ((System.currentTimeMillis() - lastPingTime) < keepAliveTimeoutMs));
347364
}
348365

349366
@Override

0 commit comments

Comments
 (0)