连载中 11/15

WebSocket 代理:Upgrade 头不传,连接必断

2026-09-27 · 13 阅读 · 0 评论 · 0 赞

握手是怎么断的

WebSocket 的连接始于一次特殊的 HTTP 请求:客户端带 Upgrade: websocket 头请求升级,服务端回 101 Switching Protocols,之后这条 TCP 连接就"改行"了,双方开始双向收发帧。问题在于 Nginx 默认按普通 HTTP 代理处理:HTTP/1.0 协议转发、不透传 Upgrade 头——后端根本收不到"要升级"的信号,连接退化成普通请求。前端的症状很典型:连接能建但很快掉,或反复重连,Network 面板里永远看不到 101。

标准配置

# Upgrade 头是"逐跳"的,代理层默认丢弃,必须手动接力
map $http_upgrade $connection_upgrade {
    default upgrade;          # 客户端要升级 → Connection: upgrade
    ""      close;            # 普通请求 → Connection: close
}

server {
    location /ws/ {
        proxy_pass http://ws_backend;
        proxy_http_version 1.1;                      # 必须 1.1,1.0 无升级机制
        proxy_set_header Upgrade    $http_upgrade;   # 透传升级头
        proxy_set_header Connection $connection_upgrade;
        proxy_read_timeout 3600s;                    # 见下文
    }
}
# map 写在 http 层;这套配置是 WebSocket 代理的标准模板,可直接抄

验证方法:DevTools 的 Network 面板过滤 WS,握手成功的标志是状态码 101;命令行用 curl -i --http1.1 -H "Upgrade: websocket" -H "Connection: Upgrade" 探一下,看到 101 才算通。

断连的两类原因

第一类:空闲超时。proxy_read_timeout 默认 60 秒——指"60 秒内没从后端读到任何数据"就断开。WebSocket 建立后若双方一直不说话,60 秒必死,表现就是"每过一分钟掉一次线"。两条路:调大超时(治标,且长超时占用连接资源)或应用层心跳(治本)。第二类:后端重启。发布、扩容、OOM 都会掐断连接,客户端要有重连机制——但重连必须带指数退避加随机抖动(1s、2s、4s……上限 30s),全量客户端同时重连等于自造 DDoS,这正是限流与熔断系列讲过的重试风暴,换个马甲又见面了。

心跳设计

// 客户端每 30 秒发一帧 ping,服务端回 pong
setInterval(() => {
    if (ws.readyState === WebSocket.OPEN) {
        ws.send(JSON.stringify({ type: "ping", ts: Date.now() }));
    }
}, 30000);
// 为什么选 30 秒:小于默认 60s 超时,留出一倍容错
// 为什么用应用层心跳而不用协议层 ping:
//   能顺带测出"连接通但业务假死",还能捎带业务数据(如未读数)

心跳间隔是超时时间的一半左右——偶尔丢一次心跳也能在超时前补上,连接不断。心跳还顺手承担了死链检测:发出去收不到回音,立刻触发重连而不是干等超时。

负载均衡的注意点

长连接 upsteam 下 ip_hash 有新意义:连接建立后流量长期粘在一台后端,轮询的"分摊"效果只在建连那一刻生效。多机部署时广播消息要走 Redis 发布订阅之类的总线(一台收到的消息要广播给连在别的机器上的用户)——这是 WebSocket 集群化的正经门槛,提前设计。出了问题怎么查?下一篇:日志与排错。

☕
503

10 年全栈工程师 · 503咖啡馆主理人

#WebSocket代理#Upgrade头#101握手#心跳#断线重连

评论 (0)

热门推荐

连载中 11/22

主从搭建实操:从零配出一主两从

光讲原理不过瘾?手把手搭一主两从:my.cnf 六个参数、复制账号、GTID、CHANGE REPLICATION SOURCE TO、SHOW REPLICA STATUS 验收,附翻车排查清单。

#MySQL#主从复制#GTID#主从搭建#高可用
2026-05-07 · 10101 阅读 · 0 评论 · 0 赞
连载中 16/22

连接池:HikariCP 参数与连接风暴

连接池不是越大越好:8 核机器配 1000 连接反而更慢的数学原理,HikariCP 四个必调参数,maxLifetime 与 wait_timeout 的隐形陷阱。

#MySQL#连接池#HikariCP#maxLifetime#连接风暴
2026-05-10 · 9873 阅读 · 0 评论 · 0 赞
连载中 4/16

缓存穿透:恶意 ID 打穿 MySQL 的四道防线

请求的数据在缓存和数据库里都不存在时,缓存形同虚设。聊聊参数校验、空值缓存、布隆过滤器、限流熔断四道防线的原理与组合打法。

#Redis#缓存穿透#布隆过滤器#高可用
2026-05-16 · 9294 阅读 · 21 评论 · 287 赞