编号:K-02 严重级:critical 工作线:SDK(sdk/*) 来源:审查 S-01、S-04、S-06、S-07、S-09、S-11、S-12、S-13、S-14、S-19、S-20、S-21、S-22 依赖:K-00 (#57)(对外语义条目) 被依赖:无
本 issue 汇总涉及 JS SDK 的条目,由 JS SDK 负责人一次改完(集中在 client.ts、mqtt.ts、types.ts)。下方"问题明细"是跨四套的原文,请只看 JS 的部分。
client.ts
mqtt.ts
types.ts
.catch(记录日志)
close
end(true)
publishUp
publishAsync
{ids, all}
keepalive: 30
taken_over
logout
not_connected
connect()
close()
unref()
index.ts
mqtt.ts:41-45
attempt++
markOffline()
types.ts:174-182
delay(attempt)
:204-206
client.ts:505-533
item.frame.rid
send_at_ms
标"补充"的条目来自第二轮 SDK 审查,总审查人已对照代码核实,证据写在条目里(没有对应的原文段落)。
sdk/js/src/client.ts、mqtt.ts、types.ts、index.ts,sdk/js/test/*,示例与 README。
sdk/js/src/client.ts
sdk/js/test/*
unhandledRejection
以下是本次复审各区审查报告的原文段落。A、M、I、P、S 开头的是原始发现编号(A 管理后台与网页、M 消息核心、I 身份认证群在线、P 传输平台部署、S SDK)。解决方案以本 issue 上方的"结论与统一方案"为准;原文里的方案与之不一致时,按上方执行。
Send
wait(timeout=None)
reconnectPeriod:0
pumpSends
send.go:170-180
rf := <-ch
send.go:141-153
!it.inflight
connect.go:57-64
client.ts:494-543
client.ts:174-177
mqtt.ts:93-99
end
client.js:380-384
:882-904
:985-987
:490-496
client.py:861-873
rid==""
client.py:605-640
client.py:323
Client.java:1000-1006
Client.java:1019-1029
Transport.java:383-391
pending.rid=""
dispatchSend
select
ch
connGone
c.ctx.Done()
sdk/go/send.go
connect.go
sdk/python/src/nixmsg/client.py
sdk/java/.../Client.java
void
await
not_found
client.ts:286,289,326,358,393,398,413-437
Error: not connected ... at Client.handleMsg
APIError: closed
exit code = 1
_request
ack()
client.py:756-758,798-803,836-851
Client.java:884-887,928-934,965-984
client.ts:352-360
receive.go:136-141,202-206
push.go:88-237,456-495,549-560
client.py
Client.java
Buffer
Buffer.from(...)
mqtt.ts:139
mqtt/dist/mqtt.esm.js
globalThis.Buffer
ReferenceError: Buffer is not defined
sdk/js/src/mqtt.ts
api.go:74-82
connect.go:148-216
client.ts:601-606
client.py:585-593
Client.java:672-683
api.go
mqtt.ts:57-69
client.js:63
Transport.java:313-321
Mqtt5Connect.DEFAULT_KEEP_ALIVE=60
.keepAlive(30)
Transport.java
Func(0)=0
auth_failed(session_invalid)
failAuth
"0x8E"
session.go:316-336
broker.go:274-292
receive.go:38-42,75-87
connect.go:120,135-146
backoff.go:27-29
transport_mqtt.go:81-92
t.stopped
client.py:652-655,735-745
Client.java:752,863-875
client.ts:207,304-311
receive.go
transport_mqtt.go
Logout
client.ts:413-437,718-725
receive.go:322-337
connect.go:232-245
client.py:913
Client.java:1055
client.py:682-689
Client.java:797-805
send.go:186-195
client.ts:528-533
client.ts:478-482
send.go:96-102
client.go:72
receive.go:201-208
client.ts:93
client.go
client.ts:323-360,378-411
receive.go:157-185
client.py:836-848
Client.java:965-977
types.ts:184-195
mqtt.ts:130-132
复审基线:main 4059a15(2026-09-30)。编号说明、各工作线的合并顺序、共享文件归属见总览 #7。
4059a15
按第二轮 SDK 审查补充 2 条(重连退避双重翻倍、限速重交复用 rid)。第一轮把 JS 退避判为"基本符合"有误,K-00 (#57) 的退避约定已同步修订。补充条目已由总审查人对照代码核实,详见正文。
已合入 origin/main 0c9b459。落地提交 55aa0cc fix: 按 K-00 约定修复 JS SDK 断线重交与退避 (#59)。
0c9b459
55aa0cc
No dependencies set.
The note is not visible to the blocked user.
编号:K-02 严重级:critical 工作线:SDK(sdk/*) 来源:审查 S-01、S-04、S-06、S-07、S-09、S-11、S-12、S-13、S-14、S-19、S-20、S-21、S-22
依赖:K-00 (#57)(对外语义条目) 被依赖:无
结论与统一方案
本 issue 汇总涉及 JS SDK 的条目,由 JS SDK 负责人一次改完(集中在
client.ts、mqtt.ts、types.ts)。下方"问题明细"是跨四套的原文,请只看 JS 的部分。.catch(记录日志);失败后的状态处理按 S-06close事件里对旧 client 调end(true),并加 generation 编号防止重复完成;断线时把在途条目置回未在途,按原 id 重交publishUp直接把字符串传给publishAsync(Uint8Array 先解码),不再用 Buffer{ids, all},每次握手成功后重发 presence.watchkeepalive: 30taken_over;fatal 后失败挂起中的其他请求(fatal 同步处理已正确)logout不收尾not_connected失败非发送请求;logout与close同样收尾connect()必须在超时后返回)close()后定时器未清理,Node 进程约 60 秒才退出(已复现)unref()index.ts导出;示例只打印令牌前缀mqtt.ts:41-45每次连接失败既attempt++又调用markOffline(),types.ts:174-182的delay(attempt)按次数翻倍,:204-206又把 base 翻倍;实际间隔在断线后约 1、4、16、30 秒,首次连接失败约 2、8、30 秒(第一轮审查判为"基本符合"有误)client.ts:505-533用item.frame.rid和预先序列化的 payload 重发)send_at_ms不变;和 S-01 的重交共用同一段代码标"补充"的条目来自第二轮 SDK 审查,总审查人已对照代码核实,证据写在条目里(没有对应的原文段落)。
改动文件
sdk/js/src/client.ts、mqtt.ts、types.ts、index.ts,sdk/js/test/*,示例与 README。与其他问题的交互 / 冲突说明
验收与测试
unhandledRejection并断言为 0:覆盖 ack 发布抛错、回调中 close、ack 回 not_found。connect()在超时后返回not_connected。send_at_ms相同。问题明细(各区审查原文,证据含文件与行号)
[S-01] 断线时已发出、未收到 resp 的发送永不重交,调用方挂起,在途额度泄漏(Go / JS / Python / Java)
Send在 ctx 没有截止时间时永久阻塞;JS 的 Promise 永远不会完成;Python 调用时若已在线,会wait(timeout=None)永久阻塞;Java 等 1 小时后抛 busy,但条目仍留在队列里。reconnectPeriod:0下,非主动断开时只清掉 volatile 回调,QoS1 publish 的回调永不触发,所以在 PUBACK 之前断线也会卡住。pumpSends直接把这条发送判为失败并出队,不会重交。send.go:170-180(publish 成功后rf := <-ch既无超时,也没有断线出口)、send.go:141-153(只挑!it.inflight)、connect.go:57-64(OnOffline 没有处理在途条目)。client.ts:494-543、client.ts:174-177、mqtt.ts:93-99(断线后新建 client,不end旧的);MQTT.jsclient.js:380-384、:882-904、:985-987、:490-496。client.py:861-873(只挑rid=="")、client.py:605-640、client.py:323。Client.java:1000-1006、Client.java:1019-1029(publish 抛异常就判失败并出队)、Transport.java:383-391。pending.rid=""),按实际状态重算在途数;帧内容保持原样(原 id、原send_at_ms)。服务器按消息号防重,会返回原结果。dispatchSend改为select三路:ch、每次连接一个的connGone、c.ctx.Done()。close事件里对旧 client 调end(true),并加 generation 编号,防止同一条被重复完成。sdk/go/send.go、connect.go;sdk/js/src/client.ts、mqtt.ts;sdk/python/src/nixmsg/client.py;sdk/java/.../Client.javasend_at_ms重发并完成;循环 150 次后仍能发送;Java 模拟 publish 抛异常,断言重交而非失败。[S-04] JS 在 Node 下确认失败会产生未处理的 Promise 拒绝,进程崩溃(JS)
void丢弃了 Promise,内部await确认请求又没有 try/catch。下面任一情况都会让 Node 进程以退出码 1 结束:close();not_found)。client.ts:286,289,326,358,393,398,413-437。Error: not connected ... at Client.handleMsg后退出;回调中调用close()时,出现APIError: closed,exit code = 1。.catch(记录日志);失败后的状态处理按 S-6。sdk/js/src/client.tsunhandledRejection并断言为 0;覆盖 ack 发布抛错、回调中 close、ack 回 not_found 三种用例。[S-06] 确认失败后没有恢复:重推被忽略、重复回执不再确认、手动 ack 吞掉失败(Python / Java / JS)
_request直接返回 NotConnected),这条回执就永远占着窗口;积满 64 条后,该发送方再也收不到新回执。ack()和 Java 的ack()失败时不报错(Java 的 future 正常完成),应用无从得知。client.py:756-758,798-803,836-851。Client.java:884-887,928-934,965-984。client.ts:352-360。receive.go:136-141,202-206。push.go:88-237,456-495,549-560。client.py、JavaClient.java、JSclient.ts[S-07] JS 在浏览器里无法连接:用了 Node 专有的全局
Buffer(JS)publishUp调用Buffer.from(...)。浏览器没有全局 Buffer(MQTT.js 浏览器包自带 Buffer,但不挂到全局;webpack 5 和 Vite 默认也不注入),所以包括 hello 在内的所有上行都抛 ReferenceError,握手永远失败并无限重连。mqtt.ts:139;mqtt/dist/mqtt.esm.js里 Buffer 只是模块内变量。globalThis.Buffer,再调publishUp,得到ReferenceError: Buffer is not defined。publishAsync(Uint8Array 先解码),不再用 Buffer。sdk/js/src/mqtt.ts[S-09] Go / JS 重连后不恢复上下线订阅(Go / JS)
api.go:74-82、connect.go:148-216;JSclient.ts:601-606;对照 Pythonclient.py:585-593、JavaClient.java:672-683。{ids, all},每次握手成功后异步重发 presence.watch。api.go、connect.go;JSclient.ts[S-11] JS 和 Java 的心跳是 60 秒,文档规定 30 秒(JS / Java)
mqtt.ts:57-69加 MQTT.jsclient.js:63;Transport.java:313-321加Mqtt5Connect.DEFAULT_KEEP_ALIVE=60。keepalive: 30,Java 设.keepAlive(30)。mqtt.ts、Transport.java[S-12] fatal 与顶号的处理时序、原因上报不一致(Go / Python / Java;Go / JS 原因名)
Func(0)=0也立刻重连,拿作废令牌连上得到 0x86,于是先上报auth_failed(session_invalid);Python/Java 随后再报一次真实原因;Go 的failAuth调 cancel 后,downLoop 在 select 里可能直接退出,fatal 原因根本没上报。JS 在收包回调里同步处理 fatal,是正确的。"0x8E",应为taken_over。session.go:316-336、broker.go:274-292;Goreceive.go:38-42,75-87、connect.go:120,135-146、backoff.go:27-29、transport_mqtt.go:81-92(只看t.stopped,fatal 没有设置它);Pythonclient.py:652-655,735-745;JavaClient.java:752,863-875;JSclient.ts:207,304-311。taken_over。receive.go、connect.go、transport_mqtt.go;Pythonclient.py;JavaClient.java;JSclient.ts[S-13] 断线时非发送请求不失败、JS 请求无超时;Go / JS 的 logout 不收尾(Go / JS;Python / Java 部分)
Logout不失败发送队列、不停止传输、不发 offline 事件;JS 的logout不失败队列、不发 offline 事件。client.ts:413-437,718-725;Goreceive.go:322-337、connect.go:232-245;Pythonclient.py:913;JavaClient.java:1055。receive.go、connect.go;JSclient.ts;Pythonclient.py、JavaClient.java(断线失败挂起请求)[S-14] 发送收到 rate_limited 后重交没有退避(Python / Java 热循环;Go / JS 固定 1 秒)
client.py:682-689;JavaClient.java:797-805;Gosend.go:186-195;JSclient.ts:528-533。[S-19] 本地整帧检查有缺口(四套)
client.ts:478-482;Gosend.go:96-102。[S-20] Go / JS 的回执去重集合没有上限(Go / JS)
client.go:72、receive.go:201-208;JSclient.ts:93。client.go、receive.go;JSclient.ts[S-21] JS 撤回事件可能重复、已撤回的消息仍交给应用(JS;手动模式四套都可能重复)
client.ts:323-360,378-411;Goreceive.go:157-185;Pythonclient.py:836-848;JavaClient.java:965-977。client.ts;手动模式四套同改[S-22] JS close() 后定时器没有清理,Node 进程约 60 秒才退出(JS)
types.ts:184-195、mqtt.ts:130-132。unref()。types.ts、mqtt.ts、client.ts复审基线:main
4059a15(2026-09-30)。编号说明、各工作线的合并顺序、共享文件归属见总览 #7。按第二轮 SDK 审查补充 2 条(重连退避双重翻倍、限速重交复用 rid)。第一轮把 JS 退避判为"基本符合"有误,K-00 (#57) 的退避约定已同步修订。补充条目已由总审查人对照代码核实,详见正文。
已合入 origin/main
0c9b459。落地提交55aa0ccfix: 按 K-00 约定修复 JS SDK 断线重交与退避 (#59)。