ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

SSE和WebSocket到底怎么选?三个维度帮你彻底搞懂技术选型

SSE和WebSocket到底怎么选?三个维度帮你彻底搞懂技术选型

前两天我在编写项目代码的时候, 遇到过一个需要实时推送的功能需求。同事直接采用了相关方案, 我查看了具体的应用场景之后, 发现这实际上就是服务端向客户端推送数据的情况, 而客户端基本不需要向服务端返回任何数据。

我提出你的做法属于杀鸡用牛刀, 使用SSE这个技术方案是完全足够的。他当时愣神了一下, 反过来问我: SSE是一种什么东西?

坦白地讲, 这种状况在软件开发的过程之中是非常普遍的。有许多项目在进行到要求“实时”的环节时, 人们的条件反射就是觉得, 如果不使用某种特定的技术手段来表现一下, 看起来就不够有专业技术水平。

然而实际上的是, 在实时通信这一个具体的领域之内, SSE这个选项和另外一种名为长轮询的技术各有着自己适用的范围, 如果选择了错误的方案, 那么不光需要编写多余的开发代码, 还有可能会导致系统内部潜伏着难以预料的故障隐患。

因为技术选型选错了, 所以后面出现了很多陷阱。

今天的这一趟讨论,咱们打算从通信的方向上去看一遍, 再从底层的协议那个层面去拆解一下, 同时也要结合实际使用的场景来详细地分析, 这样就把SSE这个事情掰开了、揉碎了讲清楚了。

当你把这所有的内容都看完了之后, 我相信你自己心里肯定就不再会有那种纠结的感觉了, 也就是说, 关于到底应该用哪一个这个问题, 你不会再有半点犹豫。

我们首先要先弄明白一个非常重要的事情, 那就是到底什么概念, 才能够被称之为实时通信。

在讨论什么是SSE之前, 有一个叫做前置概念的知识点, 大家是需要先把它统一对齐的。很多人把“实时”这个词语给理解成了“毫秒级的响应速度”, 但是, 如果站在Web开发这个行业背景的语境下面来说的话, 真实的含义其实是比较宽泛的。

它指的是什么? 它指的是当服务端手头有了新的数据的时候, 服务器端能够立刻把这个东西推送到客户端那边去, 而不是非要让客户端通过反复地发出询问请求的方式, 来主动问服务端有没有新消息。

最原本的做法叫做短轮询。客户端每隔两三秒就要发一次请求。它向服务端询问是否有新数据。就这样一遍又一遍地重复这个过程。这种方式的问题是非常明显的。大量的无效请求浪费了带宽资源。服务器的浏览器并发连接数量也很快被占满了。

另外, 数据到达时的延迟最高可能长达一个轮询的时间间隔。现在都已经是二零二六年了。还在使用短轮询的方式来实现实时推送功能的项目。如果往好了说, 那叫作稳定。如果往难听了说, 那简直就是摆烂。

所以现在呢, 我们急需一个更加不错的办法, 目的就是得让服务器端可以主动地把数据给“推”过来, 这样一来, 也就自然而然地引出下面这两位今天的主角了。

我们要说的第一个维度, 就是通信的方向。这个方向主要用来搞清楚的, 到底是哪一个人在发言。

在进行技术选型工作的时候, 有一个问题是最不应该先问自己而是应该最先考虑清楚的, 那就是在你的具体应用场景里, 客户端是否具备向服务端发送数据的实际需求呢。

服务器发送事件, 这一种东西, 它的全称就是 SSE , 全名是 Sent。把它翻译过来之后, 意思其实就是 “服务器发送事件”。这个名字, 已经把这个功能说明得够清楚、够直白了。简单点说,它就是那个服务端向着客户端单向推送数据的通道。

客户端这边, 就是通过专门的一个对象去建立起连接的。接下来, 它就只需要坐在那里等着接收起码消息就可以了。如果你非要问能不能通过这个通道发数据给服务端? 那抱歉了, 这事情SSE是不管的, 你得另外再去开一个普通的 HTTP 请求才行。

这就完全不一样了。因为这一条通道是具备全双工特点的。在进行连接建立成功之后, 客户端还有服务端都是可以非常随意地相互发送消息的。这就不需要经过什么必须一问一答的这种流程了。大家可以去把它想象成那种能够双向通行的道路。在这样的道路上, 两个方向的车辆都是可以同时行驶的。

有一个细节, 是咱们得好好说一说的。好多人的想法是这样子的: 觉得全双工这个东西, 肯定要比单向通信来得强大, 所以自己根本就不用怎么思考,直接全部选全双工的, 不就行了吗? 可是, 这个逻辑里面, 藏着一个陷阱的。那就是说, 能力越强了, 对应的实现过程, 也就得变得越复杂才行的。

你需要去手动处理断线之后又要重新连接的情况, 还需要搞心跳包来保持联系的, 还得设计消息的那些协议什么的。要是你所面对的场景的话, 只是需要服务端那边往下推送数据而已, 那么这些额外的复杂度, 简直就是纯粹地给自己在那边加戏的。

AI对话的流式输出, 这就是SSE这个技术, 在现在这个阶段用得比较普遍的典型应用场景的一个表现。

从另一个方面来看呢, 底层这个环节所使用的协议, 到底是HTTP还是TCP。

这个维度它直接就是决定了部署起来是有多难, 还有运维起来是有多艰难。

SSE是运行在HTTP协议上面的, 这里的HTTP就是大家每天都能接触到的那个HTTP。

它并不需要去进行额外的协议级别的升级工作, 也不需要服务器去做什么特殊的状态配置, 现目前所有的Web服务器环境, 比如说Nginx这种, 甚至是各位开发人员自己在本地环境中用来做开发测试用的服务器软件, 它们本身就在天生支持这样的功能上没有问题。

在后端代码这一方面, 仅仅只需要去做几个响应头的设置动作就可以了, 比如说把-Type这个字段设定为text/event-这个值, 同时还需要去关掉那个缓存的东西, 并且要保持连接的状态不被中断, 做完这些之后就可以以特定格式的文本流的方式持续地往外输出数据操作了, 而在前端那边的处理过程其实更加的简单一些, 因为现代的浏览器本身原生的环境里面就已经内置有了对应的接口API程序功能模块了, 所以一般的情况下只需要使用十几行左右数量的代码书写量就能把这个功能给跑起来并且正常运行。

并且SSE有一个非常实用的特性, 它内置了断线自动重连的功能。当浏览器发现连接中断的时候, 会自动尝试去重新建立连接。而且, 还可以通过Last-Event-ID这个机制来实现断点续传。

假如说在第50条消息发送完了以后连接断了, 那么在重连成功之后, 系统会从第51条消息开始继续接收数据, 这样就能确保一条消息都不会丢失。

这个负担就沉重得多了。尽管它在开始的时候也是借着HTTP握手的过程去建立那个连接, 但是呢, 等那个事儿一做完, 它就立刻“升级”成为一个独立的TCP协议了。

这就意味着你的服务器必须得专门去支持这个协议才行, Nginx那边需要加上一些额外的配置内容, 负载均衡器也得去做个所谓的粘性会话设置, 原因很简单, 因为如果不这么办的话, 同一个用户的连接就有可能被分到不同的后端机器上去, 还有啊反向代理这边得去调整它的超时时间参数, 反正运维团队的诸位要是听到这几个字, 脸上的表情就变得相当微妙起来了。

另外, 数据帧的开销确实是更小的, 它的包头部分仅仅是2到14字节, SSE里的每条消息都是采用纯文本格式存在的,体积相对而言是比较大的, 在高频双向通信的场景之下, 这个差距会被进行放大,但是倘若只是低频推送的话, 这样的差异是完全能够被忽略掉的。

在不同的通信协议之间, 它们的复杂程度在部署架构方面存在的差别是非常大的。

从实际的这一个场景来看, 我们究竟要做的事情是什么呢。

讲完了那些理论方面的内容, 最后终究还是要把事情落实到具体的实际情景里面去。所以呢, 我这里就按照它们出现的经常程度来进行一个排序排列。

AI对话里面的流式输出功能, 要去选择SSE这种技术。这很可能是自从2025年到现在以来, SSE应用得最广泛、也是最火的一个地方了当你在用的时候, 会发现回答的内容不是一个整体发过来的, 而是一个字一个字蹦出来的, 看起来就像一台打字机在打字一样。

这种显示效果背后的原理, 其实是SSE这个机制在起作用。

之所以会出现这样的现象, 原因是非常简单的大家需要记住: 首先, 用户会把一个问题发送给服务器, 这在技术上被视为一个普通的请求然后呢, 模型不会一次性给出全部答案, 而是把结果一个个单字单字地带出来, 这种做法从技术分类的角度来说, 属于服务端向客户端进行的单向数据推送。

整个数据流是单向的, 所以SSE完美契合这种场景。并且因为SSE是基于HTTP协议的, 所以天然就能穿透各种各样的代理和防火墙, 这样一来部署成本那就低到几乎可以忽略不计了。

所谓的实时数据看板还有系统通知这些功能, 建议大家直接去挑选使用SSE这种方案, 打个比方说像后台管理系统里那种可以实时看到订单变化的那种界面, 或者是运维团队用来监控系统状态的超级大屏, 再或者是网站给用户提供的那种消息通知推送服务, 这几个场景它们存在一个非常明显的共同特征, 那就是数据它只需要从服务器那边单方面地流向客户端这边, 用户根本不需要借助于这条通信通道来往回发任何内容, 要是选择使用SSE的话, 那么前端所需要的代码量就会变得很少, 后端开发方面也不需要再进行额外的适配操作, 而且在连接断开的情况下它还有能力自动进行重连, 这绝对算得上是“把工具用对了”的这种典型案例。

在线聊天, 选择多人协作, 比如微信网页版, 飞书文档里的多人协同编辑, 还有在线白板, 在这些场景里面, 客户端与服务端之间需要反复不停地做双向通信, 用户A打出一行字, 要把那行字发到服务端去, 然后服务端再把那个信息推送到用户B那里, 再比方说两个人同时去编辑同一份文档的话, 是需要实时地同步两个人各自做的操作改变的, 这种高频次的、双向的交互方式, 就是的主要使用领域。

直播弹幕、多人游戏——选。弹幕系统需要实时接收观众发送的消息并推送给所有人, 在游戏里每个玩家的操作都要实时同步给其他玩家。这些场景对延迟极其敏感,而且数据流是双向的, 的低开销和全双工能力在这里才能发挥价值。

一张表总结:别再选错了

在最后的时候, 把那些比较为核心的不同点给整理出来, 并且放到一个对比的表格里面去, 这样子的话呢, 是能够方便于你去进行快速的查阅和查看的。

对比项

SSE

通信方向

服务端这边发出东西之后, 这些东西是单向地去传送到客户端那儿。

双向全双工

底层协议

标准HTTP协议。

独立TCP协议(HTTP握手升级)

断线重连

浏览器内置了自动重连和断点续传的功能。

这事情得靠人去亲手搞, 要把那个指数退避加心跳的方式给做出来。

部署复杂度

极低,现有Web服务器直接支持

这种情况需要特别去安排一下相关的设置, 而且还得用到粘性会话这种机制。

数据格式

它是纯的文本形式, 并且采用的是UTF这种8位的编码格式。

文本和二进制这两种数据格式都是可以接收的。

典型场景

通过AI流式输出的方式来进行展示, 利用数据看板来进行呈现, 并且配合消息通知机制来使用。

现在的人用这种方式, 比如在线聊天, 还有协作编辑, 再或者看直播时打弹幕, 以及在玩游戏时候。

在生产环境当中, 存在一些比较容易被忽视的细节问题。

选型仅仅只是个开始的过程, 在正式投入使用之后还存在着一些需要重视的细节问题。

如果采取了SSE这种技术的话, 里面最大的一个坑就是代理缓冲区的问题——很多版本的Nginx软件都会选择对响应内容进行缓存处理, 这就造成了SSE生成的数据会先攒积到一起然后才发送给客户端使用, 这样一来流式传输的直接效果就彻底报废了。

解决问题的方法其实就是需要在后台接口返回的结果里面额外添加一个叫作X-Accel-: no的头信息标识, 这样做的目的就是通知Nginx服务器不要再进行缓存操作了。

另外, SSE的连接数是有上限的, 在HTTP/1.1的环境下, 浏览器对同一个域名的并发连接数量最多只有6个, 如果你同时开启了多条SSE通道, 那么后续的请求就不得不排队等待。

不过好在于HTTP/2的多路复用技术能够帮助解决这一问题, 从2026年启动的新项目来看, 绝大部分都已经采用了HTTP/2协议, 因此前述的限制条件现在已经基本不再构成显著的障碍。

在这一个部分, 大家是最容易把心跳保活给忽略掉的。因为长连接如果是在好长的时间里都没办法有数据传输的话, 那么在这个中间的那些代理、还有防火墙, 甚至可能是运营商的那一种叫做NAT网关的东西, 都很有可能悄悄地把这个连接给切断掉。

所以是一定需要在应用层把这个心跳机制给实现了的。就是每隔那个二三十秒的时间, 互相去发那个ping或者pong的帧。这样一来就是为了让这个连接可以一直保持活跃的那个状态。

还存在另外一种大家比较容易遇到问题的情况, 就是指的那个负载均衡组件, 要是没有进行粘性会话的这种相关设置的话, 那么处于同一个用户身份的各个连接机会很有可能发生这样的情况, 就是在其进行重新连接操作的时候, 会被分配给不一样的那个后端服务器节点, 如此一来就会导致原本保留下来的会话状态出现丢失的问题。

咱们说一些实在的心里话, 就是关于技术选型这件事, 它并不是去选择那个更强的东西, 而是去选择那个刚好足够用的东西。在使用SSE和使用不使用之间, 不存在谁好谁坏的情况, 只存在谁更适合你的实际场景的问题。

如果你的需求仅仅是想要服务端去向下推数据, 那么在众多选项中, SSE就是那个正确的选择, 因为它更简单、更轻量、也更好维护。如果你非要拿它来做单向推送的话, 这就好比开着卡车去楼下买一瓶水, 虽然你确实能到达目的地, 但是这真的没有必要。

反过来讲, 如果你的实际应用场景确实需要实现双向的、实时进行通信操作的功能, 那么这种情况下的技术选择就是一种无法被替代的存在。

甚至在某些规模非常大的应用系统内部, 这两种技术方案是可以相互配合着共同使用的: 具体做法是采用SSE这个技术手段专门去接收来自AI模型的流式输出内容, 同时采用其他的处理方式去处理用户端的实时状态同步任务, 让每一个技术节点都承担起自己应该承担的责任, 做好各自分内的工作, 这种分工明确的做法, 才是符合架构设计正确原则的最优解决方案。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进