跳转到主内容
趣航编程网 - 趣学编程,启航技术之路!

为什么Python脚本连接WebSocket断开频繁_实现心跳检测与自动重连机制

WebSocket频繁断开主因是未显式保活,需成对设置ping_interval与ping_timeout(如45s/30s),并确保Nginx、内核conntrack等中间设备超时值匹配,同时维持recv循环或定时ping,避免静默关闭。 频繁断开不是代码写错了,而是没告诉连接“我还活着”——WebSocket 本身不保活,空闲超时后中间设备(Nginx、防火墙、内核 conntrack)会直接掐断 TCP 连接。 ping_interval 和 ping_timeout 必须成对设置 只设
ping_interval=20
不够,
websockets
库默认
ping_timeout=20
,但网络延迟或服务端响应慢时,pong 回不来就会触发强制关闭(错误码
1011
或直接
ConnectionClosedError
)。
ping_timeout
必须 > 网络 RTT + 服务端处理时间,生产环境建议设为
30
ping_interval
要比中间设备的空闲超时小至少 10 秒,比如 Nginx 默认
proxy_read_timeout=60
,那就设
45
; 两者都得显式传给
connect()
,不能只靠
run_forever()
(那是旧版
websocket-client
的用法); asyncio 版必须用
await websocket.ping()
手动发,且要包在
try/except
里,否则一次失败就崩协程。 recv() 循环不能漏,空连接撑不过 5 秒 asyncio 下的 WebSocket 不是“连上就自动活着”,底层依赖持续有协程在 await 消息或 ping。如果业务只发不收,又没启动接收循环,连接会在几秒内被静默关闭(日志常见
code=1001
)。 哪怕不处理消息,也得写一个最小 recv 占位:
async for _ in websocket:
while True: await websocket.recv()
; 若确定永不收消息,改用
await asyncio.sleep(1)
配合定时
ping()
,但 sleep 时间别超过
ping_interval
; 绝对不要在 recv 循环外另起线程调
send()
,会破坏 event loop,引发
RuntimeError: There is no current event loop
或 CPU 暴涨。 重连不能裸 try/except ConnectionClosedError 捕获到异常后立刻
await connect()
,会导致旧连接残留、socket 句柄泄漏、重试风暴打挂服务端。 Python 3.14.3 微软官方的 Python 扩展,是 VS Code 安装量最高的扩展(209M+)。集成 IntelliSense(通过 Pylance)、调试(通过 Python Debugger)、代码检查、格式化、重构和单元测试等功能。支持 Jupyter Notebook、虚拟环境管理和多 Python 版本切换。 下载 立即学习 “ Python免费学习笔记(深入) ”; 先检查
websocket.closed
,再调
await websocket.close()
await
完成; 重连前必须
await asyncio.sleep(1)
,避免高频重试; 退避策略用
min(30, 2 ** attempt)
,上限卡死 30 秒,否则弱网下越等越长; 重连逻辑要用
asyncio.create_task()
启动,别
await
它,否则阻塞主连接循环。 nginx 和内核 conntrack 是隐藏断连元凶 客户端和 Python 服务端代码全对,连接仍每 60 秒断一次?八成是 nginx 或 Linux 内核在背后动手。 Nginx 默认
proxy_read_timeout 60
,需在 location 块加:
proxy_read_timeout 86400;
proxy_send_timeout 86400;
; Linux 内核对“非标准流”连接(如长 WebSocket)常缩短
nf_conntrack_tcp_timeout_established
到 300–600 秒,查当前值:
sysctl net.netfilter.nf_conntrack_tcp_timeout_established
,临时调高:
sudo sysctl -w net.netfilter.nf_conntrack_tcp_timeout_established=7200
; 所有代理层(CDN、云 WAF)都要确认是否支持 WebSocket 长连接,有些会静默丢弃 ping 帧。 真正难搞的从来不是代码逻辑,而是你根本看不到的中间链路——ping/pong 能跑通不代表连接能活过 60 秒,得一层层确认从 Python 协程、到系统 socket、再到反向代理、最后到云厂商的超时配置,全都对齐了才行。

相关文章