HTTP/2 + SSE 能否完全替代 Websocket吗?
服务器发送事件 (SSE) 是一种标准化协议,允许 Web 服务器将数据推送到客户端而无需使用替代机制,例如: ping、长轮询 (Long Polling) 或 WebSocket。服务器发送事件(SSE)是 HTML5 公布的一种服务器向浏览器客户端发起数据传输的技术。一旦创建了初始连接,事件流将保持打开状态 直到客户端关闭。该技术通过传统的 HTTP 发送,并具有 WebSocket 缺乏的
大家好,很高兴又见面了,我是"前端技术进阶",由我带着大家一起关注前端前沿、深入前端底层技术,大家一起进步,也欢迎大家关注、点赞、收藏、转发!
1. 什么是 SSE(Server-Sent Events)
服务器发送事件 (SSE) 是一种标准化协议,允许 Web 服务器将数据推送到客户端而无需使用替代机制,例如: ping、长轮询 (Long Polling) 或 WebSocket。

服务器发送事件(SSE)是 HTML5 公布的一种服务器向浏览器客户端发起数据传输的技术。一旦创建了初始连接,事件流将保持打开状态 直到客户端关闭。该技术通过传统的 HTTP 发送,并具有 WebSocket 缺乏的各种功能,例如:自动重新连接、 事件 ID 以及 发送任意事件 的能力。
📢 SSE 本身并没有提供自动重连的机制,其所谓的自动重连特性是指 ` 浏览器自动处理与服务器的连接断开并尝试重新连接的过程 `。当然开发者也可以通过 `eventSource.retry = 5000` 手动重连。
SSE 就是利用服务器向客户端声明,接下来要发送的是流信息(streaming),会连续不断地发送过来。这时,客户端不会关闭连接,会一直等着服务器发过来的新的数据流,可以类比视频流。SSE 就是利用这种机制,使用流信息向浏览器推送信息。其基于 HTTP 协议,目前除了 IE/Edge,其他浏览器都支持。
Content-Type: text/event-stream
// 必须是 UTF-8 编码的文本,流本质就是下载
Cache-Control: no-cache
Connection: keep-alive
使用 SSE 可以 显著节省便携式设备的带宽和电池寿命,并且可以与现有的基础设施配合使用,因为其直接通过 HTTP 协议运行而无需像 WebSockets 那样进行连接升级。
2.SSE 如何借力 HTTP2 补齐与 WebSocket 的短板
由于 SSE 是基于 HTTP 的,其天然适配于 HTTP/2,这样 SSE 就可以集两者之长:HTTP/2 可以基于多路复用流形成一个高效传输层,同时 SSE 给应用提供了 API 使之能够进行推送 。
📢 流就是一个独立的、在客户端服务器之间的 HTTP/2 连接上双向的帧序列,其最重要的一个特征就是 ` 单个 HTTP/2 连接可以包含多个并发开启的流 `,其中每个端点都交错着来自多个流的帧。

假如应用使用 HTTP/1 传输,此时 NetWork 选项卡可能会有如下的输出:

浏览器会并行打开多个 HTTP/1.x 连接来加速页面加载,而 不同的浏览器针对同一域名并发打开的连接数量有不同的限制,基本上都会支持 6 个左右不同的连接。
为了克服这个限制,类似于 域名分片的技术就被用来将资源分布在多个域名上。这些技术(可以将其认为是非法入侵)包括 JavaScript 和 CSS 文件、图像和资源内联,在 HTTP/2 世界中反而适得其反。这可能是迁移到 HTTP/2 时受到的最主要的影响了,即 消除多年以来所做的优化。
当使用 HTTP/2 的时候,NetWork 中会看到浏览器使用单个多路复用的连接,带来更快的加载时间。

而 SSE 是基于 HTTP 的,这意味着使用 HTTP/2 的时候,不仅仅可以在一个 TCP 连接上交错多个 SSE 流,同时还可以将多个 SSE 流(服务器到客户端推送)与多个客户端请求(客户端到服务器)交错。
const http2 = require('http2');
const fs = require('fs');
// 创建 HTTP/2 服务器
const server = http2.createSecureServer({
key: fs.readFileSync('server-key.pem'),
cert: fs.readFileSync('server-cert.pem')
});
server.on('stream', (stream, headers) => {
const path = headers[':path'];
const method = headers[':method'];
if (path === '/events' && method === 'GET') {
// 设置响应头
stream.respond({
'content-type': 'text/event-stream',
':status': 200
});
// 发送一条欢迎消息
stream.write(`data: Welcome! Current time is ${new Date().toISOString()}\n\n`);
// 每 5 秒发送一条消息
const intervalId = setInterval(() => {
stream.write(`data: Current time is ${new Date().toISOString()}\n\n`);
}, 5000);
// 清理工作
stream.on('close', () => {
clearInterval(intervalId);
});
} elseif (path === '/send-data' && method === 'POST') {
// 客户端通过 fetch('https://localhost:3000/send-data') 发送消息到服务端
let body = '';
stream.on('data', chunk => {
body += chunk.toString();
});
stream.on('end', () => {
console.log('Received data:', body);
stream.respond({':status': 200});
stream.end(JSON.stringify({ status: 'success', data: body}));
});
} else {
stream.respond({':status': 404});
stream.end();
}
});
// 监听端口
server.listen(3000, () => {
console.log('Server running at https://localhost:3000/');
});
// 下面是浏览器连接 HTTP/2 的示例
const eventSource = new EventSource('https://localhost:3000/events');
// 监听消息事件
eventSource.onmessage = function(event) {
const messageDiv = document.getElementById('messages');
const newMessage = document.createElement('div');
newMessage.textContent = `New message: ${event.data}`;
messageDiv.appendChild(newMessage);
};
// 监听连接打开事件
eventSource.onopen = function(event) {
console.log('Connection opened');
};
// 监听错误事件
eventSource.onerror = function(event) {
if (eventSource.readyState === EventSource.CLOSED) {
console.log('Connection closed');
} else {
console.log('Error occurred, attempting to reconnect...');
}
};
// 发送 HTTP/2 请求
document.getElementById('sendButton').addEventListener('click', () => {
fetch('https://localhost:3000/send-data', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({message: 'Hello, server!'})
})
.then(response => response.json())
.then(data => {
console.log('Response:', data);
})
.catch(error => {
console.error('Error:', error);
});
});
比如上面的 http2 代码示例:
-
独立的请求和响应:由于 HTTP/2 支持多路复用,每个请求和响应都是独立的流,因此客户端可以同时通过一个流接收 SSE 消息而另一个流发送 POST 请求。
-
双向通信:HTTP/2 支持双向通信,允许服务器和客户端在任何时候发送和接收数据。这意味着客户端可以在接收 SSE 消息的同时,通过另一个流发送数据到服务器。
有了 HTTP/2 和 SSE 就可以使用一个纯 HTTP 双向连接,加之使用简单的 API 使应用代码注册多个服务器推送。
双向能力的缺失一直是 SSE 对比 Websocket 时最主要的短板。有了 HTTP/2 就弥补了这个短板,从而为跳过 Websocket 并坚持使用基于 HTTP 的机制提供了可能。
3.HTTP/2 + SSE 能完全替代 Websocket 吗?
答案是 No,主要是 Websocket 已经被大量应用,同时在某些特定应用场景下,其底层设计致力于双向能力,拥有较少的负载的优势就会体现出来。假设需要在双端之间交互大吞吐量的消息,其中上下流动的消息量大致差不多(比如,需要保持所有玩家同步的大型多人在线游戏),这种场景下 Websocket 可能会是更好的选择。
如果考虑像是展示 实时市场新闻、市场数据、聊天应用 等场景的时候,依赖 HTTP/2 + SSE 会提供高效的双向通信通道并保有留在 HTTP 世界的大量优势:
-
Websocket 由于是 将一个 HTTP 连接升级到一个完全不同的,与 HTTP 协议没有任何关系的协议,所以当考虑将其与现有 Web 基础设施做兼容的时候就比较难受了。
-
扩展性和安全:Web 基础设施组件(防火墙,入侵检测、负载均衡)已经基于 HTTP 建立、维护和设置了,这是一个大型 / 关键应用在弹性、安全性和可扩展性等方面更加友好的环境。
因此 HTTP/2 + SSE 能否完全替代 Websocket 的结论是:
-
HTTP/2 并非 HTTP 的完整替代
-
类似于 域名分片、资源内联和图片拼接 等入侵入式技术在 HTTP/2 世界中正好适得其反
-
HTTP/2 不是类似于 Websocket 或者 SSE 这样的推送技术的替代品,HTTP/2 推送只能被浏览器处理而非应用
-
将 HTTP/2 和 SSE 结合起来提供高效的基于 HTTP 的双向通信
Websocket 技术可能会继续使用,但是 SSE 和其 EventSource API 同 HTTP/2 的能力相结合可以在多数场景下达到同样的效果,而且会更简单。
参考资料
https://zhuanlan.zhihu.com/p/37365892
https://blog.csdn.net/cnweike/article/details/116056371
https://www.infoq.com/articles/websocket-and-http2-coexist/
https://stackoverflow.com/questions/48344634/why-do-we-need-sse-when-we-have-http2-bidirectional-streaming
火山引擎开发者社区是火山引擎打造的AI技术生态平台,聚焦Agent与大模型开发,提供豆包系列模型(图像/视频/视觉)、智能分析与会话工具,并配套评测集、动手实验室及行业案例库。社区通过技术沙龙、挑战赛等活动促进开发者成长,新用户可领50万Tokens权益,助力构建智能应用。
更多推荐
所有评论(0)