LOGO OA教程 ERP教程 模切知识交流 PMS教程 CRM教程 开发文档 其他文档  
 
网站管理员

HTTP/2 + SSE 能否完全替代 Websocket吗?

admin
2025年5月1日 11:47 本文热度 52

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-cacheConnection: 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



阅读原文:原文链接


该文章在 2025/5/6 12:43:08 编辑过
关键字查询
相关文章
正在查询...
点晴ERP是一款针对中小制造业的专业生产管理软件系统,系统成熟度和易用性得到了国内大量中小企业的青睐。
点晴PMS码头管理系统主要针对港口码头集装箱与散货日常运作、调度、堆场、车队、财务费用、相关报表等业务管理,结合码头的业务特点,围绕调度、堆场作业而开发的。集技术的先进性、管理的有效性于一体,是物流码头及其他港口类企业的高效ERP管理信息系统。
点晴WMS仓储管理系统提供了货物产品管理,销售管理,采购管理,仓储管理,仓库管理,保质期管理,货位管理,库位管理,生产管理,WMS管理系统,标签打印,条形码,二维码管理,批号管理软件。
点晴免费OA是一款软件和通用服务都免费,不限功能、不限时间、不限用户的免费OA协同办公管理系统。
Copyright 2010-2025 ClickSun All Rights Reserved