速查表:WebSocket 长连接工程清单

本表把整套 WebSocket 与长连接实战教程压缩成可勾选的清单:六组表格覆盖协议、服务端、客户端、代理与部署、观测、压测,每行一句话要点。用法有两种——上线前逐项打勾,故障时按分组倒查。

一、协议层(Protocol)

检查项一句话要点
握手请求GET + Upgrade: websocket + Connection: Upgrade + Sec-WebSocket-Version: 13,缺一即 400
握手响应必须返回 101 Switching Protocols 与 Sec-WebSocket-Accept,返回 200 说明中间层吃掉了 Upgrade
Accept 校验base64(sha1(key + 258EAFA5-E914-47DA-95CA-C5AB0DC85B11)),服务端必须校验
帧结构FIN + opcode + mask + payload len + 掩码键;客户端发往服务端的数据帧必须掩码
控制帧ping/pong/close 的 payload 不得超过 125 字节,不能分片
消息上限服务端显式限制单帧与单消息长度(如 64KB),超限直接关闭并记原因
关闭码1000 正常、1001 离开、1002 协议错误、1008 策略拒绝、1011 服务端错误
子协议通过 Sec-WebSocket-Protocol 协商版本,为老客户端兼容留好升级路径
HTTP/2标准 WebSocket 不支持 h2,网关必须按 HTTP/1.1 处理 Upgrade 请求
文本校验opcode=1 的载荷必须是合法 UTF-8,否则关闭连接(1007)

二、服务端(Server)

检查项一句话要点
读写分离一条连接两个协程:读侧串行读,写侧独占写,其他协程只往写队列投递
注册与注销注册成功后立刻 defer unregister,鉴权失败、超限等分支也要关闭连接
读超时设置 SetReadDeadline,并在 pong 处理里续期,否则心跳正常也会整批掉线
写超时每次写都设 SetWriteDeadline,避免慢客户端把写协程钉死
心跳保活服务端 30s 发一次 ping,pongWait 设为 2~2.5 倍间隔;NAT/LB 空闲超时须大于它
鉴权握手期校验令牌,连接级权限缓存并设过期;支持踢下线与单点登录
背压写队列必须有界;队列满时按策略丢弃、降级或摘除慢客户端,禁止阻塞广播循环
推送语义单播查路由、组播查房间并去重、广播分批限速且可中断
路由表uid -> 连接集合 带 TTL 并定期续期,避免幽灵路由造成静默丢消息
消息去重全局递增 msg_id + 客户端去重表,实现至少一次投递下的幂等
扩容网关无状态、连接有状态;连接数按一致性哈希分片,扩容时按比例迁移
连接上限单机设置连接上限保护,达到阈值时拒绝新连接并回 503,而不是把内存耗光
优雅关闭先停止 accept,再通知客户端重连,最后 drain 后关闭,配合 defer 统一回收

三、客户端(Client)

检查项一句话要点
重连退避指数退避 + 随机抖动(如 1s 起、上限 30s、抖动 ±20%),防止重连风暴
重连次数设置最大次数与「网络恢复即重试」的事件触发,避免无脑循环
会话恢复重连后带 session_id 与本地最大 seq,服务端补发缺口消息
消息补拉补拉与实时推送会重叠,必须靠 msg_id 去重
心跳参与客户端必须响应 ping 并主动发心跳,不能只收不发
令牌刷新长连接期间令牌可能过期,重连前先刷新,避免 401 循环
页面切换移动端切后台、浏览器标签页休眠都会断开,恢复时要立即重建
多标签页同浏览器多标签共享一条连接(SharedWorker)或做去重,避免多端重复渲染
发送队列断线期间的发送要排队并设上限,恢复后按序补发
本地时钟延迟统计使用相对时间(RTT),不要依赖客户端绝对时间

四、代理与部署(Proxy & Deploy)

检查项一句话要点
Upgrade 透传Nginx proxy_http_version 1.1 + proxy_set_header Upgrade $http_upgrade + Connection "upgrade"
读超时代理 proxy_read_timeout 必须大于心跳间隔;默认 60s 是断连事故的常客
缓冲开关关闭 proxy_buffering,否则推送会被缓冲延迟
多级链路LB、网关、Nginx 每一跳都要放开 Upgrade 与超时,逐跳验证 101
真实 IP透传 X-Forwarded-For / X-Real-IP,用于鉴权、限流与封禁
TLS 终结优先在接入层终结,内部用明文减少开销;在服务端终结可上 mTLS
证书与会话配置完整证书链;开启会话复用降低握手开销,注意证书轮换不停机
健康检查检查接口不要走 WebSocket,用普通 HTTP 探针,避免探针占满连接槽
限流位置建连限流放在接入层(按 IP/uid),消息限流放在应用层(按 uid)
发布方式灰度按实例分批,先摘流量再 drain 连接,客户端重连到新实例
回滚老客户端必须能连新服务端,协议新增字段一律向后兼容

五、观测(Observability)

检查项一句话要点
在线指标im_ws_online_connections、im_ws_online_users,分实例采集
质量指标disconnects_total{reason}、heartbeat_rtt_seconds、建连失败率
延迟指标push_duration_seconds{target} 取 P99,分单播/组播/广播
背压指标写队列长度分布、dropped_total{reason} 丢弃率
资源指标go_goroutines、process_open_fds、fd 使用率、内存/连接
日志字段conn_id、uid、gateway、remote_ip、握手耗时、断开原因、在线时长
基数纪律uid、conn_id、room_id 不进 label,只进日志与链路追踪
大盘分层总览一屏、网关分实例、客户端按版本、接入层 101 与 upstream 超时
告警阈值在线数 5 分钟跌 20% → P0;建连失败率 > 1% → P1;推送 P99 > 500ms → P1;fd > 80% → P1
阈值纪律带时间窗口、带流量下界、按实例分组,并对采集失败单独告警

六、压测(Benchmark)

检查项一句话要点
三层分开建连、保活、收发分别压,混在一起得到的数字没有意义
握手基线先用 curl --http1.1 -H 'Upgrade: websocket' ... 确认返回 101
工具选型k6 ws 压 WebSocket;ghz 压内部推送 RPC;wrk 只能压 HTTP 接入层
台阶加压逐级升到拐点,观察「连接数涨而吞吐不涨、延迟陡增」的那一刻
恢复段必须包含降压段,验证 fd、协程、内存能回落到基线
压测机瓶颈独立节点、提高 ulimit -n、加源 IP 或开 tcp_tw_reuse 避免端口耗尽
同步观测压测时同轴采集 ss -s、sar -n DEV、pprof 三类数据
容量公式单机容量 = min(内存/每连接内存, fd 上限, CPU, 带宽),实测再留 2 倍余量
报告模板连接数、msg/s、P50/P95/P99、内存/连接、CPU、错误率、拐点位置

常用命令速查

# 握手验证:拿到 101 才算通过
curl --http1.1 -i -N -H 'Connection: Upgrade' -H 'Upgrade: websocket' \
  -H 'Sec-WebSocket-Version: 13' -H 'Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==' \
  'http://127.0.0.1:8080/ws?token=test'
# 连接与资源:三者应互相对得上
ss -s ; ss -tan state established | wc -l
lsof -p PID | wc -l ; ls /proc/PID/fd | wc -l ; cat /proc/PID/status | grep Threads
# 泄漏定位:栈快照 + 堆差分
go tool pprof http://127.0.0.1:6060/debug/pprof/goroutine
go tool pprof -base before.heap 'http://127.0.0.1:6060/debug/pprof/heap'
# 压测
k6 run ws-bench.js
ghz --insecure -c 50 -n 200000 --proto ./im.proto --call im.PushService.Push 10.0.0.12:9090

关键内核与系统参数

参数建议值作用
net.core.somaxconn32768已完成握手的 accept 队列长度
net.ipv4.tcp_max_syn_backlog65535半连接队列,重连风暴时最先撞到
ulimit -n / LimitNOFILE1048576单进程 fd 上限,systemd 与 shell 两处都要改
net.ipv4.tcp_keepalive_time300应用层心跳失效时的内核兜底
net.ipv4.tcp_tw_reuse1主动关闭方复用 TIME_WAIT,压测机受益明显
net.netfilter.nf_conntrack_max≥ 2 × 连接数连接跟踪表,满了会随机丢包
net.core.rmem_max / wmem_max16MB大缓冲,配合每连接读写缓冲设置
fs.file-max2097152系统级 fd 上限,进程上限不能超过它

上线前红线

  1. 握手失败、鉴权失败、超限拒绝三类路径都必须关闭连接并记原因。
  2. 所有读写都有超时,不存在「永远等待」的调用。
  3. 写队列必须有界且队列满有明确策略;断开原因按 reason 拆分采集,能回答「为什么掉」。
  4. 每一跳代理的 Upgrade 透传与超时已逐跳验证。
  5. 单机连接上限、fd 上限、内核参数写入配置管理而非手工生效;压测报告与容量结论已归档。
  6. 优雅发布与回滚演练通过,老客户端能连新服务端。

小结:协议层守握手与帧规范,服务端守读写分离、心跳、鉴权、背压、路由与优雅关闭,客户端守退避重连、补拉与去重,代理层守 Upgrade 透传与超时,观测层守在线数、质量、延迟与资源四类指标,压测层守三层分离与拐点定容——六组清单逐项打勾,长连接服务的主要事故面就被覆盖完了。

笔记加载中…