告别轮询,SSE 流式传输可太香了!

嗨!我是小谷,大家好久不见~

今天想和大家分享的一个技术是 SSE 流式传输。如标题所言,通过 SSE 流式传输的方式可以让我们不再通过轮询的方式获取服务端返回的结果,进而提升前端页面的性能。对于需要轮询的业务场景来说,采用 SSE 确实是一个更好的技术方案。

接下来,我将从 SSE 的概念、与 Websocket 的对比、SSE 应用场景多个方面介绍 SSE 流式传输,感兴趣的同学一起来了解下吧!

什么是 SSE 流式传输

SSE 全称为 Server-sent events , 是一种基于 HTTP 协议的通信技术,允许服务器主动向客户端(通常是Web浏览器)发送更新。它是 HTML5 标准的一部分,设计初衷是用来建立一个单向的服务器到客户端连接,使得服务器可以实时地向客户端发送数据。

这种服务端实时向客户端发送数据的传输方式,其实就是流式传输。

我们在与 ChatGPT 交互时,可以发现 ChatGPT 的响应总是断断续续完成。细扒 ChatGPT 的网络传输模式,可以发现,用的也是流式传输。

SSE 流式传输的好处

在 SSE 技术出现之前,我们习惯把需要等待服务端返回的过程称为长轮询。长轮询的实现也是借助 http 请求来完成的,一个完整的长轮询过程如下图所示:

从图中可以发现,长轮询最大的弊端是当服务端响应请求之前,客户端发送的所有请求都不会被受理。并且服务端发送响应的前提是客户端发起请求。

前后端通信过程中,我们常采用 ajax 、axios 来异步获取结果,这个过程,其实也是长轮询的过程。

而同为采用 http 协议通信方式的 SSE 流式传输,相比于长轮询模式来说,优势在于可以在不需要客户端介入的情况下,多次向客户端发送响应,直至客户端关闭连接。这对于需要服务端实时推送内容至客户端的场景可方便太多了!

SSE 技术原理

1. 参数设置

前文说到,SSE 本质是一个基于 http 协议的通信技术,因此想要使用 SSE 技术构建需要服务器实时推送信息到客户端的连接,只需要将传统的 http 响应头的 contentType 设置为 text/event-stream 。

并且为了保证客户端展示的是最新数据,需要将 Cache-Control 设置为 no-cache 。

在此基础上,SSE 本质是一个 TCP 连接,因此为了保证 SSE 的持续开启,需要将 Connection 设置为 keep-alive 。

Content-Type: text/event-stream
Cache-Control: no-cache
Connection: keep-alive

完成了上述响应头的设置后,我们可以编写一个基于 SSE 流式传输的简单 Demo。

 2. SSE Demo

服务端代码:

const express = require('express');
const app = express();
const PORT = 3000;app.use(express.static('public'));app.get('/events', function(req, res) {res.setHeader('Content-Type', 'text/event-stream');res.setHeader('Cache-Control', 'no-cache');res.setHeader('Connection', 'keep-alive');let startTime = Date.now();const sendEvent = () => {// 检查是否已经发送了10秒if (Date.now() - startTime >= 10000) {res.write('event: close\ndata: {}\n\n'); // 发送一个特殊事件通知客户端关闭res.end(); // 关闭连接return;}const data = { message: 'Hello World', timestamp: new Date() };res.write(`data: ${JSON.stringify(data)}\n\n`);// 每隔2秒发送一次消息setTimeout(sendEvent, 2000);};sendEvent();
});app.listen(PORT, () => {console.log(`Server running on http://localhost:${PORT}`);
});

客户端代码

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><title>SSE Example</title>
</head><body><h1>Server-Sent Events Example</h1><div id="messages"></div><script>const evtSource = new EventSource('/events');const messages = document.getElementById('messages');evtSource.onmessage = function(event) {const newElement = document.createElement("p");const eventObject = JSON.parse(event.data);newElement.textContent = "Message: " + eventObject.message + " at " + eventObject.timestamp;messages.appendChild(newElement);};</script>
</body>
</html>

当我们在浏览器中访问运行在 localhost: 3000 端口的客户端页面时,页面将会以流式的模式逐步渲染服务端返回的结果:

需要注意的是,为了保证使用 SSE 通信协议传输的数据能被客户端正确的接收,服务端和客户端在发送数据和接收数据应该遵循以下规范:

服务端基本响应格式

SSE响应主要由一系列以两个换行符分隔的事件组成。每个事件可以包含以下字段:

data:事件的数据。如果数据跨越多行,每行都应该以data:开始。
id:事件的唯一标识符。客户端可以使用这个ID来恢复事件流。
event:自定义事件类型。客户端可以根据不同的事件类型来执行不同的操作。
retry:建议的重新连接时间(毫秒)。如果连接中断,客户端将等待这段时间后尝试重新连接。

字段之间用单个换行符分隔,而事件之间用两个换行符分隔。

客户端处理格式

客户端使用EventSource接口监听SSE消息。例如,使用JavaScript:

const evtSource = new EventSource('path/to/sse');evtSource.onmessage = function(event) {console.log(event.data); // 处理收到的数据
};

 SSE 应用场景

SSE 作为基于 Http 协议由服务端向客户端单向推送消息的通信技术,对于需要服务端主动推送消息的场景来说,是非常适合的:

SSE 兼容性

可以发现,除了IE和低版本的主流浏览器,目前市面上绝大多数浏览器都是支持 SSE 通信技术的。

SSE 与 WebSocket 对比 

看完 SSE 的使用方式后,细心的同学应该发现了,SSE 的通信方式和 WebSocket 很像啊,而且 WebSocket 还支持双向通信呢?为什么不直接使用 WebSocket ?下表展示了两者之间的对比

SSE

WebSocket

协议

基于HTTP,使用标准HTTP连接

单独的协议(ws:// 或 wss://),需要握手升级

通信方式

单向通信(服务器到客户端)

全双工通信

数据格式

文本(UTF-8编码)

文本或二进制

重连机制

浏览器自动重连

需要手动实现重连机制

实时性

高(适合频繁更新的场景)

非常高(适合高度交互的实时应用)

浏览器支持

良好(大多数现代浏览器支持)

非常好(几乎所有现代浏览器支持)

适用场景

实时通知、新闻feed、股票价格等需要从服务器推送到客户端的场景

在线游戏、聊天应用、实时交互应用

复杂性

较低,易于实现和维护

较高,需要处理连接的建立、维护和断开

兼容性和可用性

基于HTTP,更容易通过各种中间件和防火墙

可能需要配置服务器和网络设备以支持WebSocket

服务器负载

适合较低频率的数据更新

适合高频率消息和高度交互的场景

可以发现,SSE 与 WebSocket 各有优缺点,对于需要客户端与服务端高频交互的场景,WebSocket 确实更适合,但对于只需要服务端单向数据传输的场景,SSE 确实能耗更低,且不需要客户端感知。

参考文档

https://developer.mozilla.org/zh-CN/docs/Web/API/Server-sent_events

demo 地址

git@github.com:dossweet/SSE-demo.git

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/636334.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【Linux 杂货铺】进程间通信

1.进程为什么要通信呢&#xff1f; ①&#x1f34e; 为了进程之间更好的协同工作&#xff0c;举个例子&#xff0c;在学校&#xff0c;学院的管理人员给教师安排课程的时候&#xff0c;必须事先知道该教师平常的上课情况&#xff0c;不然会将教师的课程安排到一起造成麻烦&…

安装mathutils方法

使用blenderproc的时候&#xff0c;需要安装mathutils&#xff0c;但是直接pip会出现问题&#xff0c;这里记录一下如何安装。 官方安装方法 这里是mathutils的库&#xff0c;里面写了安装方式&#xff1a; mathutils 3.3.0 on PyPI - Libraries.io git clone https://gitl…

【GIS教程】ArcGIS做日照分析(附练习数据下载)

我国对住宅日照标准的规定是:冬至日住宅底层日照不少于1小时或大寒日住宅层日照不少于2小时(通常以当地冬至日正午12时的太阳高度角作为依据)。因冬至日太阳高度角最低&#xff0c;照射范围最小&#xff0c;如果冬至日12&#xff1a;00建筑物底层能够接收到阳光&#xff0c;那么…

记录一个hive中跑insert语句说没创建spark客户端的问题

【背景说明】 我目前搭建离线数仓&#xff0c;并将hive的执行引擎改成了Spark&#xff0c;在将ods层的数据装载到dim层&#xff0c;执行insert语句时报如下错误 【报错】 [42000][40000] Error while compiling statement: FAILED: SemanticException Failed to get a spark…

一文学会 ts 构建工具 —— tsup

文章目录 能打包什么&#xff1f;安装用法自定义配置文件条件配置在 package.json 中配置多入口打包生成类型声明文件sourcemap生成格式自定义输出文件代码分割产物目标环境支持 es5编译的环境变量对开发命令行工具友好监听模式 watch提供成功构建的钩子 onSuccess压缩产物 min…

史上最全的四分之一、半车再到全车7自由度常规悬架建模与仿真之一

一、悬架建模的简化过程 汽车是一个复杂的振动系统&#xff0c;针对不同的需求进行不同的简化。在对悬架振动分析中&#xff0c;把汽车车身看做一个刚体&#xff0c;把驾驶员座椅和驾驶员拿掉&#xff1b;车身以下至车轮之间的橡胶垫&#xff0c;连接杆&#xff0c;弹簧等具有…

Veritas Backup Exec 23.0 (Windows) - 面向中小型企业的数据备份和恢复

Veritas Backup Exec 23.0 (Windows) - 面向中小型企业的数据备份和恢复 请访问原文链接&#xff1a;Veritas Backup Exec 23.0 (Windows) - 面向中小型企业的数据备份和恢复&#xff0c;查看最新版。原创作品&#xff0c;转载请保留出处。 作者主页&#xff1a;sysin.org Ba…

线性代数基础2矩阵

矩阵是什么 矩阵就是二维数组&#xff0c;下面是一个 m 乘 n 的矩阵&#xff0c;它有 m 行&#xff0c;n 列&#xff0c;每行每列上面都有元素&#xff0c;每个元素都有行标i 和列标 j&#xff0c; a ij 。简称m n矩阵&#xff0c;记作&#xff1a; 注意a11的索引是 A[0,0]。…

leetcode:438. 找到字符串中所有字母异位词

给定两个字符串 s 和 p&#xff0c;找到 s 中所有 p 的 异位词 的子串&#xff0c;返回这些子串的起始索引。不考虑答案输出的顺序。 异位词 指由相同字母重排列形成的字符串&#xff08;包括相同的字符串&#xff09;。 示例 1: 输入: s "cbaebabacd", p "…

使用Python爬取易车网汽车信息(含x-sign参数逆向分析)

文章目录 1. 写在前面2. 接口分析3. 断点分析3. 算法还原 【&#x1f3e0;作者主页】&#xff1a;吴秋霖 【&#x1f4bc;作者介绍】&#xff1a;擅长爬虫与JS加密逆向分析&#xff01;Python领域优质创作者、CSDN博客专家、阿里云博客专家、华为云享专家。一路走来长期坚守并致…

Java如何实现List的反转

hi&#xff0c;我是程序员王也&#xff0c;一个资深Java开发工程师&#xff0c;平时十分热衷于技术副业变现和各种搞钱项目的程序员~&#xff0c;如果你也是&#xff0c;可以一起交流交流。 今天我们来聊聊Java中实现List反转~ List反转的基本概念 在Java中&#xff0c;反转一…

RK3568 学习笔记 : u-boot 通过 tftp 网络更新 u-boot自身

前言 开发板型号&#xff1a; 【正点原子】 的 RK3568 开发板 AtomPi-CA1 使用 虚拟机 ubuntu 20.04 收到单独 编译 RK3568 u-boot 使用 rockchip Linux 内核的设备树 【替换】 u-boot 下的 rk3568 开发板设备树文件&#xff0c;解决 u-boot 下千兆网卡设备能识别但是无法 Pi…