Skip to content

feat(platform): 控制连接 WS ping/pong 保活 + 半开探测#396

Merged
deepcoldy merged 1 commit into
masterfrom
feat/ws-control-keepalive
Jul 7, 2026
Merged

feat(platform): 控制连接 WS ping/pong 保活 + 半开探测#396
deepcoldy merged 1 commit into
masterfrom
feat/ws-control-keepalive

Conversation

@deepcoldy

Copy link
Copy Markdown
Owner

背景

用户反馈:长时间不访问平台后,第一次访问失败、刷新就好。根因是控制连接空闲期被 LB/NAT 静默掐成半开——daemon 这头发现不了(app 心跳是单向 daemon→平台消息,掩盖不了平台→daemon 方向断),等你访问时平台发 open-stream 到不了 daemon → 首次失败;等 TCP 慢慢超时重连、或刷新那会儿刚好重连上了 → 又好了。

改动

控制连接加 WS 层 ping/pong(tunnel-client.ts,adoptControl 后):

  • 空闲也每 30s ping,平台自动回 pong → 双向都有流量,防 LB/NAT idle 掐断
  • 一个周期没等到 pong → terminate() 触发重连(~30-60s 探到半开,不用干等几分钟 TCP 超时)。

配合平台侧同款 ping/pong(内网 platform 仓另提)。纯 WS 控制帧,不动 JSON 协议、不涉两仓字节兼容。

验证(真机 e2e)

本地起平台(带 server ping/pong) + 跑 startPlatformTunnelClient(带本改动):

  • 保活:连上后跨 2 个 ping 周期(68s)稳定——0 次误判、0 崩溃;
  • 半开探测kill -STOP 冻住平台模拟半开(不发 FIN),daemon ~52s 内日志 控制连接 ping 无 pong,判定半开,重连 并重连。
    tsc 通过。

长时间不访问平台后第一次访问失败的元凶:控制连接空闲期被 LB/NAT 静默掐成半开,daemon
这头发现不了(心跳是单向应用消息、掩盖不了平台→daemon 方向断),访问时平台发 open-stream
到不了 daemon → 首次失败。加 WS 层 ping/pong:空闲也定时 ping(平台自动回 pong,双向有流量
防 idle 掐断),一个周期没等到 pong 就 terminate 触发重连(~30-60s 发现,不用干等几分钟 TCP 超时)。

真机 e2e 验证:连上后跨 2 个 ping 周期保活稳定(无误判/无崩溃);kill -STOP 冻住平台模拟半开,
daemon ~52s 内探到 ping 无 pong 并重连。

@deepcoldy deepcoldy left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Codex review: checked src/platform/tunnel-client.ts keepalive lifecycle around adoptControl/cleanupSock/close. I don't see a blocker. pingTimer is owned at the tunnel-client instance level and cleared by cleanupSock on close/stop/unbound before reconnect; the interval has an OPEN guard and terminate is caught. pongAlive is per adopted socket closure, so reconnects don't share liveness state. The one-missed-pong logic sends ping only after setting pongAlive=false and terminates on the next interval if no pong arrived, which gives a full 30s response window and should not race normal pong handling. terminate() flows through close -> cleanupSock -> scheduleReconnect, and scheduleReconnect's reconnectTimer guard prevents duplicate reconnect timers. pnpm build passes locally. Proceeding with the requested admin squash merge.

@deepcoldy deepcoldy merged commit e88f656 into master Jul 7, 2026
1 check passed
@deepcoldy deepcoldy deleted the feat/ws-control-keepalive branch July 7, 2026 10:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant