对话的边界:HTTP 的克制,SSE 的流淌,WebSocket 的自由
写在前面
“前后端交互,不就是前端调后端接口,后端返回 JSON 吗?”
这是大多数后端开发者的第一反应。我们习惯了 HTTP 请求-响应模式,却忽略了“数据交互”还有另外两种重要的方式。直到我接触 RAG 项目,需要实现“逐字输出”的效果时,才发现:原来 SSE(Server-Sent Events)可以让服务端像打字机一样把内容推给前端。
后来做直播间弹幕,又接触了 WebSocket:服务器能主动推送,客户端也能随时发消息,真正的“双向实时”。
这三种技术,就像三种不同的“对话方式”:
HTTP:你问一句,我答一句。问完结束。
SSE:你说开始,我就一直说,你只听不说。
WebSocket:咱们随时可以说,谁想说话就说,像打电话。
今天,我们就从原理到实战,彻底搞清这三者的区别、优缺点和适用场景。读完这篇,你不仅能对付面试官,还能在实际项目中做出更合理的技术选型。
一、HTTP:最熟悉的“一问一答”
1.1 工作原理
HTTP(HyperText Transfer Protocol)是建立在 TCP 之上的应用层协议,核心特征是请求-响应模型:
客户端(如浏览器)发起一个请求
服务端处理请求,返回响应
连接关闭(短连接),或保持一段时间(Keep-Alive)
1.2 实现方式(Java)
@RestController public class UserController { @GetMapping("/api/user") public User getUser() { return new User("张三", 18); } }1.3 优缺点
1.4 适用场景
普通 CRUD 接口
RESTful API
文件上传/下载
不需要实时更新的页面(后台管理系统)
如果你需要“服务器主动发消息”或“实时双向通信”,HTTP 就不够用了。
二、SSE:服务端单向推送的“广播员”
SSE(Server-Sent Events)是 HTML5 规范的一部分,允许服务端通过 HTTP 协议单向向客户端推送事件流。客户端通过EventSourceAPI 接收。
你体验过的“AI 逐字输出”(ChatGPT 式打字效果),背后就是 SSE。
2.1 工作原理
客户端发起一个特殊的 HTTP 请求(
Accept: text/event-stream)服务端保持连接打开,并定期或按需发送
data:格式的消息客户端通过
onmessage回调逐条接收,连接可以一直保持
2.2 实现方式(Java + Spring)
Spring 从 4.2 开始支持 SSE,使用SseEmitter:
@RestController public class SseController { @GetMapping(value = "/stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE) public SseEmitter stream() { SseEmitter emitter = new SseEmitter(); // 异步线程推送 executor.execute(() -> { for (int i = 0; i < 10; i++) { emitter.send("消息 " + i); Thread.sleep(1000); } emitter.complete(); }); return emitter; } }前端接收:
const eventSource = new EventSource('/stream'); eventSource.onmessage = (event) => { console.log('收到:', event.data); };2.3 优缺点
2.4 适用场景
AI 流式对话(RAG、ChatGPT 式逐字输出)
实时股票行情、体育比分
服务器日志实时展示
新闻推送、系统通知
在 RAG 项目中用 SSE 做流式输出,正是最佳实践——既实现了实时感,又比 WebSocket 简单得多。
三、WebSocket:真正的全双工“电话线”
WebSocket 是一个独立的协议(ws:// / wss://),它在 HTTP 握手后升级为持久连接,实现了客户端和服务端之间任意时刻的双向通信。
3.1 工作原理
客户端发起 HTTP 请求,头部包含
Upgrade: websocket服务端同意升级,返回
101 Switching Protocols连接升级为 WebSocket 协议,之后双方可以随时互发消息
3.2 实现方式(Java + Spring WebSocket)
@Configuration @EnableWebSocketMessageBroker public class WebSocketConfig implements WebSocketMessageBrokerConfigurer { @Override public void registerStompEndpoints(StompEndpointRegistry registry) { registry.addEndpoint("/ws").withSockJS(); } @Override public void configureMessageBroker(MessageBrokerRegistry registry) { registry.enableSimpleBroker("/topic"); registry.setApplicationDestinationPrefixes("/app"); } }前端(使用 STOMP 协议):
const socket = new SockJS('/ws'); const stompClient = Stomp.over(socket); stompClient.connect({}, () => { stompClient.subscribe('/topic/room/123', (msg) => { console.log('弹幕:', msg.body); }); }); // 发送弹幕 stompClient.send('/app/chat', {}, JSON.stringify({content: '666'}));3.3 优缺点
3.4 适用场景
直播间弹幕、聊天室
在线游戏(实时位置同步)
协同编辑文档(如 Google Docs)
实时监控仪表盘
需要双向消息的任何应用
直播间弹幕,确实是非 WebSocket 不可的场景——因为客户端需要随时发送,服务端也要随时广播给所有人。
四、三者对比:一张表看懂选型
选型决策图
五、面试常见问题及回答思路
问:SSE 和 WebSocket 有什么区别?什么时候用 SSE,什么时候用 WebSocket?
答:SSE 是单向推送(服务端→客户端),基于 HTTP,实现简单,自动重连,适合流式输出、通知推送。WebSocket 是全双工双向通信,协议独立,支持二进制,适合聊天室、游戏等场景。如果只需要服务端推送,选 SSE;如果需要客户端也能随时发消息,选 WebSocket。
问:SSE 和长轮询(Long Polling)有什么区别?
答:长轮询是客户端发起请求,服务端挂起连接直到有数据才返回,然后客户端立即再发起新请求。SSE 是真正的长连接,服务端可以连续发送多个事件,无需客户端反复请求。SSE 更高效,延迟更低。
六、实践建议:如何选择和使用?
默认用 HTTP:绝大多数场景 HTTP 足够,不要为了“实时”而提前优化。
只要服务端推数据,优先选 SSE:比 WebSocket 简单很多,且基于 HTTP,运维友好。
需要双向实时交互,再考虑 WebSocket:比如你做的直播间弹幕。
注意连接数限制:SSE 和 WebSocket 都会占用长连接,浏览器同域名限制 6 个,服务端也需要考虑并发上限。
降级方案:WebSocket 可以用 SockJS + STOMP 降级到 HTTP 流式或长轮询。
在日常开发中,我习惯这样组合:
普通 API → HTTP
AI 对话 / 进度通知 → SSE
即时通讯 / 游戏 → WebSocket
总结:没有最好,只有最合适
HTTP、SSE、WebSocket 并不是替代关系,而是不同场景下的最佳工具。理解它们的区别,就像知道什么时候用卡车、什么时候用轿车、什么时候用摩托车——都能载人,但效率完全不同。
下次你接到一个需求:
只需要前端调后端接口 → HTTP
需要服务器像打字机一样输出 → SSE
需要双向实时聊天 → WebSocket
选对了,代码写起来顺手,用户体验也好。选错了,要么大炮打蚊子,要么马车跑高速。
假设你要实现一个“在线考试系统”,需要实时显示剩余时间、实时接收老师发布的公告,但考生不需要发送消息(除了提交答案)。同时,系统需要支持数千人同时考试。你会选择 HTTP、SSE 还是 WebSocket?为什么?欢迎在评论区写下你的技术选型和理由。
