出了什么事 / What happened
我在跑 OpenClaw 的 WebSocket 网关。进程活着,端口在听,openclaw gateway health 是绿的。
但时不时,客户端一连上来,连接就挂了。不是进程挂。是连接挂了。静默的。客户端报"网关不可用",运维跑 openclaw gateway status 一切正常。
I was running OpenClaw's WebSocket gateway. Process alive, port listening, openclaw gateway health green. But intermittently, client connections would silently drop. Not process crash — connection crash. Client reports "gateway unavailable", ops dashboard shows all green.
原则 / Principle
网关在客户端和服务之间。对任何请求它有两种正确反应:
处理它 — 路由、处理、响应。
拒绝它 — 认证不对、协议不对、超过负荷——返回错误。
不管客户端发来什么——畸形数据、慢握手、时机不对——网关要么处理,要么干净地拒绝。它不应该让客户端侧的状况把自己搞挂。
OpenClaw 的网关违反了这个原则。
A gateway sits between clients and services. For any request, it has two correct responses: handle it or reject it. It should never let a client-side condition crash itself. OpenClaw's gateway violates this principle.
查到了什么 / What I found
握手慢了一点?挂。
握手超时写死 3 秒。机器忙一点,3 秒不够。连接默默掉了,没有错误消息,没有关闭码。客户端看到"不可用"。网关只是慢了一点——负载下完全正常。
本地连接正常关闭?挂。
关闭码 1000 是 RFC 6455 定义的"正常关闭"。在 loopback 上是日常。但 OpenClaw 只处理 "pairing required" 做 fallback,其他全当致命错误。
OpenClaw 官方没有修 / Still unfixed upstream
这些不是我独家发现的。都是社区用户在 OpenClaw GitHub 上提的真实 issue,至今未关闭:
| Issue | 问题 / Description |
|---|---|
| #46892 | 握手 3 秒超时太激进,busy event loop 下连接被 1000 关掉 |
| #47931 | DEFAULT_HANDSHAKE_TIMEOUT_MS 硬编码,建议改 10s 并可配置,官方未采纳 |
| #46097 | Raspberry Pi 5 上 100% 失败率——ARM64 初始化 7-8 秒 vs 3 秒超时 |
| #45222 | 本地回环 WebSocket 间歇性失败,busy 网关上 60%+ 失败率 |
| #48032 | node-host 断开后不重试直接退出,单次暂时故障杀死服务 |
| #24777 | 重试无退避,每秒一次紧循环 600+ 次 |
OPENCLAW_TEST_HANDSHAKE_TIMEOUT_MS——但它被 process.env.VITEST 门控了,生产环境用不了。修复的机制就在代码里,但被锁在了测试标志后面。There's actually an env var
OPENCLAW_TEST_HANDSHAKE_TIMEOUT_MS in the code — but it's gated behind process.env.VITEST. The fix mechanism exists, locked behind a test flag.
我的修复 / My fix
既然官方没修,我做了一个 overlay 工具来补丁网关。让它按应有的方式工作:处理请求或拒绝请求,但不要自己挂掉。
Since upstream won't fix it, I built an overlay tool. Make the gateway work as it should: handle or reject, never crash.
| 之前:崩 / Before | 之后:处理或拒绝 / After |
|---|---|
| 握手 > 3s → 默默死掉 | 等到 8s(可配置),然后干净超时 |
| Challenge 慢 → 连接被杀 | 等到 6s(可配置),然后返回错误 |
| Loopback 1000/1006 → 致命崩溃 | 识别为正常 → 走 local fallback |
| 网络抖 → 永久失败 | 指数退避重试,3 次后干净失败 |
| VITEST 门控的环境变量 | 生产环境解锁 |
| 手改文件,更新后丢失 | 可重复的 apply / check / rollback |
用起来 / Usage
pip install .
gateway-stability apply \
--install-root /usr/local/lib/node_modules/openclaw \
--overlay-root ~/.gateway-stability/overlays/gateway-stability \
--env-file ~/.gateway-stability/.env
幂等。跑两次和跑一次一样。每次 openclaw update 后重新跑一次就行。
Idempotent. Running twice is the same as running once. Re-apply after each openclaw update.
环境变量 / Environment Variables
GW_STABILITY_HANDSHAKE_TIMEOUT_MS=8000
GW_STABILITY_CONNECT_CHALLENGE_TIMEOUT_MS=6000
GW_STABILITY_PRECONNECT_RETRY_ATTEMPTS=3
GW_STABILITY_PRECONNECT_RETRY_MIN_DELAY_MS=250
GW_STABILITY_PRECONNECT_RETRY_MAX_DELAY_MS=1500
GW_STABILITY_PRECONNECT_RETRY_BACKOFF=2
项目信息 / Project Info
GitHub · Wiki · Release v0.1.0
Python 3.9+ · 零外部依赖 / Zero dependencies · MIT License · 中英文双语文档