【实时篇】WebSocket 与 SSE:长连接的选型与运维

5 阅读4分钟

在《前端视角下的网络协议》系列中,我们讨论的几乎都是“请求-响应”模式。但在实时聊天、金融看板或 AI 大模型输出场景下,这种模式会显得非常被动。

为了实现服务器主动向前端推送数据,我们需要建立长连接。作为全栈开发者,面对 WebSocketSSE,你不仅要会写代码,更要理解它们在协议层面的差异以及在生产环境中的运维成本。


一、 WebSocket:全双工的“双向奔跑”

WebSocket 是一个独立于 HTTP 的协议,它实现了浏览器与服务器之间的全双工通信

1. 握手与升级(Upgrade)

WebSocket 的连接始于一个标准的 HTTP 请求。浏览器通过在 Header 中加入 Upgrade: websocket 告知服务器:“我想换个协议”。

  • 101 Switching Protocols:一旦服务器同意,TCP 连接将保持开启,双方从此进入 WebSocket 的二进制/文本帧交互。

2. 核心优势

  • 真正的双向性:客户端和服务器可以同时互发消息,延迟极低。
  • 协议头极小:建立连接后,数据帧的头部开销远小于 HTTP。

3. 运维痛点

  • 不兼容 HTTP 代理:某些旧的防火墙或负载均衡器(LB)无法识别这种协议切换。
  • 状态维护:服务器需要维护成千上万个持久的 TCP 连接,对内存和并发处理能力有极高要求(参见 Node.js 篇中的 UV_THREADPOOL_SIZE 调优)。

二、 SSE (Server-Sent Events):单向的“顺风车”

SSE 并不是一种全新的协议,它是基于 HTTP 协议的一项技术(通常是 text/event-stream 内容类型)。

1. 协议特征

  • 单向推送:只有服务器能推给客户端,客户端想说话还得另发 HTTP 请求。
  • 打字机效果的福音:因为它是基于流(Streaming)的,非常适合 ChatGPT 这种逐字输出的场景。

2. 核心优势

  • 轻量级:原生支持断线重连(retry 机制),无需像 WebSocket 那样手写重连逻辑。
  • 兼容性强:它就是标准的 HTTP 请求,能轻松穿过任何防火墙、代理和 Nginx。
  • 开发成本低:在 Node.js 中只需设置几个 Response Header 即可。

三、 硬核对比:该选哪一个?

维度WebSocketSSE
通讯模式全双工 (双向)单向 (服务器 -> 客户端)
底层协议WebSocket (从 HTTP 升级)纯 HTTP
重连机制需要开发者手动实现浏览器原生内置支持
最大连接数受限于服务器内存和端口受浏览器限制(HTTP/1.1 只有 6 个,HTTP/2 无限)
数据格式支持二进制和文本仅支持 UTF-8 文本

四、 生产环境的运维实战

作为 8 年全栈,上线长连接应用时,你必须解决以下两个问题:

1. 代理层的“断连”陷阱

  • Nginx 超时:默认情况下,Nginx 可能会切断长时间没有数据往来的连接。

    • 对策:设置 proxy_read_timeout。同时在应用层实现心跳机制(Heartbeat) ,每隔 30 秒发一个 Ping/Pong 包,告诉 Nginx“我还活着”。
  • HTTP/2 的多路复用:如果你使用 SSE,务必开启 HTTP/2。否则,一个 SSE 连接会占满 6 个并发连接中的一个,导致页面其他资源加载受阻。

2. 水平扩展与消息分发

当你的 Node.js 服务有多个副本时,客户端 A 连在 Server 1,客户端 B 连在 Server 2。Server 1 怎么把消息发给 B?

  • 解决方案:引入 Redis Pub/Sub。服务器接收到推送请求后,先发给 Redis 频道,所有订阅了该频道的服务器副本再寻找自己名下的客户端进行推送。

💡 前端开发者的硬核贴士

  • AI 场景选 SSE:如果只是做聊天机器人输出,不要用 WebSocket,SSE 的性能更优且开发极简。
  • 双向交互选 WebSocket:如实时多人协作编辑(类似 Google Docs)或高频交易系统。
  • 不要忘记 Close:在页面销毁(onUnmounted)时,务必手动关闭长连接,否则会造成严重的服务器连接泄漏。

结语

长连接协议将 Web 应用从“静态拉取”推向了“实时响应”。理解了 WebSocket 的力量与 SSE 的简洁,你才能在复杂的业务需求中选出性能最优、成本最低的通信方案。