fix(feishu): invoke SDK disconnect() directly and surface cleanup failures

This commit is contained in:
matevip 2026-05-25 21:31:09 +08:00
parent 54e3f7f3fa
commit e9ead959b8

View File

@ -548,15 +548,20 @@ public class FeishuChannelAdapter extends AbstractChannelAdapter implements Stre
}
/**
* 关闭 WebSocket 连接
* 关闭 WebSocket 连接
* <p>
* 必须主动关闭底层 WebSocket 连接并触发 SDK 的内部清理停止 pingLoop
* 释放 ExecutorService仅置空引用会导致旧连接的 pingLoop 和线程池
* 持续运行造成文件描述符和线程泄漏最终使新连接无法建立
* <p>
* SDK {@code disconnect()} protected 无法直接调用通过反射
* 获取内部 {@code conn} 字段并发送 close(1000) 触发 SDK
* {@code onClosed disconnect()} 清理链路
* SDK 内部 {@code disconnect()} 这条单一入口完成 cleanup 它本身就是
* SDK {@code onClosed} 回调里调用的方法包含
* {@code conn.close(1000) executor.shutdown() 字段清零} 的全套动作
* 该方法在 SDK 2.6.1 中是 {@code protected}外部需通过反射 + setAccessible
* 调用SDK 2.7.1 起新增了 public {@code close()}届时整段反射可以删除
* <p>
* 反射失败例如未来 SDK 重命名 {@code disconnect} WARN 级别记录而非
* 静默吞掉避免泄漏在升级后悄无声息地复发
*/
private void stopWebSocket() {
cancelSilentDisconnectWatchdog();
@ -567,17 +572,14 @@ public class FeishuChannelAdapter extends AbstractChannelAdapter implements Stre
}
if (wsClient != null) {
try {
// 反射获取 protected conn 字段发送 close(1000) 触发 SDK 清理
var connField = wsClient.getClass().getDeclaredField("conn");
connField.setAccessible(true);
Object conn = connField.get(wsClient);
if (conn != null) {
// conn OkHttp WebSocketclose(1000) 会触发 onClosed disconnect()
var closeMethod = conn.getClass().getMethod("close", int.class, String.class);
closeMethod.invoke(conn, 1000, "client closed");
}
var disconnect = wsClient.getClass().getDeclaredMethod("disconnect");
disconnect.setAccessible(true);
disconnect.invoke(wsClient);
} catch (NoSuchMethodException e) {
log.warn("[feishu] SDK Client.disconnect() not found — WebSocket cleanup skipped, "
+ "leak may recur until SDK API is re-verified: {}", e.getMessage());
} catch (Exception e) {
log.debug("[feishu] Error during WebSocket disconnect: {}", e.getMessage());
log.warn("[feishu] WebSocket disconnect failed: {}", e.getMessage());
}
}
wsClient = null;