WebSocket 与长连接
HTTP 是“请求响应”模式,服务端不能主动推送。WebSocket 把连接升级为全双工长连接。面试主线:握手怎么升级、心跳怎么保活、和替代方案怎么选。
握手:HTTP 升级
WS 握手: 普通 HTTP 请求升级为长连接
- 握手就是普通 HTTP 请求带
Upgrade: websocket头,服务端回 101 切换协议 - 之后同一 TCP 连接上双向发帧(文本/二进制)
- 关键头:
Sec-WebSocket-Key(客户端随机)→ 服务端算Sec-WebSocket-Accept返回(防缓存代理误升级)
为什么需要心跳
长连接空闲时可能被中间设备(NAT、防火墙、LB)静默断开,双方都不知道:
- 被动关闭:客户端发的包被 NAT 丢弃,卡死等超时(体验差)
- 解法:应用层心跳(ping/pong 帧或自定义),周期互发,超时判定死连接并重连
客户端 → ping → 服务端 → pong(或业务消息)
N 秒没收到 pong → 判定断线 → 重连生产要点:心跳间隔要小于中间设备空闲超时(LB 常见 60s,心跳 30s 左右);服务端也要统计最近活跃时间,超时主动踢。
三种推送方案对比
| 方案 | 方向 | 机制 | 适用 |
|---|---|---|---|
| 轮询 | 客户端拉 | 定时 HTTP 请求 | 简单,延迟和流量是矛盾 |
| SSE | 服务端推 | HTTP 长响应(text/event-stream) | 单向推送(通知、补全) |
| WebSocket | 双向 | 升级后的全双工 | 聊天、协作、实时交互 |
选型:单向推送用 SSE(更简单,自动重连),双向交互用 WebSocket。轮询是兜底(兼容、简单)。
生产注意
- 多实例:WS 连接粘在单实例,广播要 Redis pub/sub(见 FastAPI 生产部署篇)
- 连接数:每连接有内存开销,单机万级要规划;心跳风暴要限频
- 网关:Nginx/LB 要配 WS 支持(Upgrade 头透传、空闲超时调大)
面试追问
- WebSocket 握手是什么? 普通 HTTP 请求带 Upgrade 头,服务端回 101。之后同一 TCP 双向通信
- 为什么需要心跳? 中间设备静默断连接,双方无感知。心跳探测 + 超时重连
- SSE 和 WebSocket? SSE 单向(HTTP 长响应、自动重连),WS 双向。单向推送用 SSE 更简单
- 多实例怎么广播? WS 粘在单实例,用 Redis pub/sub 跨实例分发(见 FastAPI 篇)
- 心跳间隔怎么定? 小于中间设备空闲超时(LB 常见 60s 就设 30s 左右),太频繁是浪费