WebSocket 连接在固定时间后被断开,客户端反复重连,close code 多为 1006。
连接在空闲约 30/60/350 秒后断开,浏览器或客户端自动重连;DevTools 显示 close code 1006(异常关闭,未收到 close frame)。Nginx 日志出现 upstream prematurely closed connection,后端可能完全没有收到关闭原因。
先确认断开时间是否固定,以及哪一层先发 FIN/RST:
websocat -v --ping-interval 20 wss://app.example.com/ws
tcpdump -i any -nn 'tcp port 443 and (tcp[tcpflags] & (tcp-fin|tcp-rst) != 0)'
ss -tnp '( dport = :443 or sport = :443 )' | grep -i estab
conntrack -L -p tcp --dport 443 | head
再看 Nginx 的超时与 Upgrade 配置:
grep -E 'upstream prematurely closed|client prematurely closed|timed out' /var/log/nginx/error.log | tail
nginx -T | grep -E 'proxy_read_timeout|proxy_send_timeout|keepalive_timeout|proxy_http_version|Upgrade'
正确的代理配置至少包含:
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_read_timeout 3600s;
proxy_send_timeout 3600s;
Kubernetes Ingress 需要同步注解:
nginx.ingress.kubernetes.io/proxy-read-timeout: "3600"
nginx.ingress.kubernetes.io/proxy-send-timeout: "3600"
云 LB 的 idle timeout 常是元凶:ALB 默认 60 秒、NLB 与 NAT 网关常见 350 秒。可用 aws elbv2 modify-load-balancer-attributes --load-balancer-arn <arn> --attributes Key=idle_timeout.timeout_seconds,Value=3600 调整。
易误判:TCP keepalive 不能保活 HTTP/WebSocket 连接 —— LB 的空闲计时看应用数据,keepalive 探测常常不被转发。close code 1006 只表示“没收到 close frame”,不能据此判断是客户端还是服务端主动断开;要看 FIN 方向和中间层日志。固定 60 秒断开不一定是 Nginx,也可能是 ALB 默认 idle timeout;固定 350 秒更像 NLB/NAT。
proxy_read_timeout 小于心跳间隔,空闲连接被关闭。Upgrade/Connection 头或 proxy_http_version 1.1,升级失败或行为异常。proxy_read_timeout/proxy_send_timeout,K8s 用 Ingress 注解同步。proxy_http_version 1.1,必要时关闭代理缓冲。terminationGracePeriodSeconds 覆盖排空时间。proxy_read_timeout;350 秒更像 NLB/NAT 的空闲超时。