当业务开始要求“服务端主动推消息”时,普通的请求响应模型就不够用了。Node.js 在实时通信场景下很常见,但很多人第一次接触 WebSocket 时只记住了 API,却没有真正建立连接模型和场景边界。这一篇会从长连接思维出发,把几类实时通信方式做一轮对比。
为什么普通请求响应模型不够?
普通 HTTP 最适合的,是客户端发起请求,服务端返回结果,然后这次交互结束。但有些业务天然不满足这个节奏,比如:
- 聊天消息;
- 实时通知;
- 在线协作;
- 状态同步;
- 长时间任务进度推送。
这些场景的关键不是“谁来发起一次请求”,而是“状态一变化,另一边要尽快知道”。这就是长连接和服务端推送的意义所在。
如果继续强行用轮询顶替所有实时需求,当然也能跑,但你通常会很快在三个地方感到吃力:
- 请求量明显增多;
- 状态更新存在感知延迟;
- 客户端和服务端都在做大量“明明没变化却还要问一次”的无效工作。
这也是为什么一旦业务进入实时场景,连接模型本身就会成为架构问题。
WebSocket 和 SSE 各自适合什么?
WebSocket 更适合双向实时通信场景。也就是说,不只是服务端要推,客户端本身也需要持续发消息并保持交互,比如聊天、协作、互动游戏、实时控制等。
SSE 则更适合服务端单向推送的场景。它在某些实现上更简单,更适合通知流、进度推送、状态广播这类“主要由服务端持续输出”的问题。
所以,两者并不是谁替代谁,而是:
- 双向实时交互偏向
WebSocket; - 单向持续推送偏向
SSE。
除此之外,选择时还可以再看几个更现实的判断维度:
- 客户端是否也需要频繁主动发消息;
- 是否希望尽量复用 HTTP 语义和基础设施;
- 消息量级和广播规模大概如何;
- 断线恢复和重连成本是否可接受。
很多时候,真正合适的方案不是“永远上 WebSocket”,而是根据通信方向和复杂度选择最小可用模型。
连接建立以后真正的难点是什么?
很多实时通信的麻烦并不在于“能不能连上”,而在于连接持续存在以后怎么管理。比如:
- 心跳和保活;
- 断线重连;
- 客户端状态恢复;
- 广播范围控制;
- 连接何时清理。
也就是说,实时通信一旦进入生产环境,问题就不再是某个 API 调用,而是连接生命周期管理。
特别是在用户可能频繁切后台、网络抖动、页面刷新或多端同时在线的场景里,你还得回答更多问题:旧连接何时作废?重连后状态如何恢复?同一个用户多个连接之间如何同步?这些都不是“连上就算完成”的问题。
实时系统的工程难点通常在哪里?
随着规模变大,你很快还会遇到:
- 多实例之间的消息同步;
- 某条消息是否真的送达;
- 某类连接是否能被正确回收;
- 扩容以后连接状态如何协调;
- 监控和告警要看哪些指标。
这也是为什么实时系统从来不只是“把消息推过去”,而是要有完整的连接管理和状态协调意识。
一旦进入多实例部署,你通常还会开始碰到发布策略问题。因为连接是长时间存在的,服务滚动重启时如何优雅摘流、如何避免所有连接瞬间断开、如何让消息在多个节点之间正确广播,都会直接影响体验。
实时通信方案设计时最值得先想清楚什么?
一个更稳妥的设计顺序,通常不是先写连接代码,而是先回答:
- 这是单向推送还是双向互动;
- 消息是否需要确认送达;
- 状态是否需要持久化和补发;
- 连接断开后客户端应该如何恢复;
- 是否真的需要实时,而不是秒级刷新也能接受。
把这些前提先厘清,你就会更容易判断是该用 SSE、WebSocket,还是简单轮询就足够。实时系统的复杂度很高,越早收紧边界,后面越容易稳住。
总结
这一篇我们把实时通信的基本边界拉清楚了:长连接存在是因为普通 HTTP 不能覆盖所有交互模式,WebSocket 和 SSE 分别适合不同类型的问题,而真正困难的地方则是连接建立之后的生命周期管理与系统协调。
下一篇我们会把视角从通信层抬高到交付层,讨论一个 Node 服务怎么真正稳定地跑起来。