网关防崩补丁 / Gateway Stability Patch

网关不应该因为收到一个请求就挂掉。
A gateway should handle requests or reject them — not crash because of them.
for OpenClaw · GitHub · MIT License · Python 3.9+ · 2026.03

出了什么事 / 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 秒不够。连接默默掉了,没有错误消息,没有关闭码。客户端看到"不可用"。网关只是慢了一点——负载下完全正常。

确认响应晚了一拍?挂。

WebSocket 打开后的 connect-challenge 超时也是写死的,窗口不可配置。负载一高就超时,超时就断。不重试。

本地连接正常关闭?挂。

关闭码 1000 是 RFC 6455 定义的"正常关闭"。在 loopback 上是日常。但 OpenClaw 只处理 "pairing required" 做 fallback,其他全当致命错误。

网络抖了一下?挂。

预连接函数没有任何重试。一次 DNS 抖动、端口没就绪——和永久故障同等对待。用户手动重试立刻成功。每次。

OpenClaw 官方没有修 / Still unfixed upstream

这些不是我独家发现的。都是社区用户在 OpenClaw GitHub 上提的真实 issue,至今未关闭:

Issue问题 / Description
#46892握手 3 秒超时太激进,busy event loop 下连接被 1000 关掉
#47931DEFAULT_HANDSHAKE_TIMEOUT_MS 硬编码,建议改 10s 并可配置,官方未采纳
#46097Raspberry Pi 5 上 100% 失败率——ARM64 初始化 7-8 秒 vs 3 秒超时
#45222本地回环 WebSocket 间歇性失败,busy 网关上 60%+ 失败率
#48032node-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 · 中英文双语文档