什么是 SSE 流式传输?
SSE(Server-Sent Events)技术是一种用于实现服务器主动向客户端推送数据的技术,也被称为“事件流”(Event Stream)。
1、技术原理
SSE基于HTTP协议,利用了其长连接特性,在客户端与服务器之间建立一条持久化连接,并通过这条连接实现服务器向客户端的实时数据推送。客户端向服务器发送一个GET请求,带有指定的header,表示可以接收事件流类型,并禁用任何的事件缓存。服务器返回一个响应,带有指定的header,表示事件的媒体类型和编码,以及使用分块传输编码(chunked)来流式传输动态生成的内容。服务器在有数据更新时,向客户端发送一个或多个名称:值字段组成的事件,由单个换行符分隔,事件之间由两个换行符分隔。
2、数据格式
服务器可以发送事件数据、事件类型、事件ID和重试时间等字段。每个字段都必须以换行符(\n)结尾,并且每个消息都必须以两个换行符(\n\n)结尾。具体格式如下:
- event:可选字段,用于指定事件的名称,客户端可以通过这个名称识别不同类型的事件。
- data:必须字段,用于指定数据的内容,可以是文本、JSON等类型。
- id:可选字段,用于指定事件的标识符,客户端可以通过这个ID识别和处理事件。
- retry:可选字段,用于指定客户端在连接断开后重新连接的时间间隔(以毫秒为单位)。
3、客户端实现
import React, { FC, useEffect, useState } from 'react';
import { LineChart, Line, XAxis, YAxis, CartesianGrid, Tooltip, Legend, ResponsiveContainer } from 'recharts';
// recharts 图表需要安装
const App: FC = () => {
const [data, setData] = useState<any[]>([]);
useEffect(() => {
// 创建一个EventSource对象,用于从/api/sse接口接收
const source = new EventSource('/api/sse');
// 链接成功
source.onopen = () => {
console.log('连接成功');
};
// 报错信息
source.onerror = (err) => {
console.log(err);
};
// 获取数据
source.onmessage = (res) => {
setData(prevData => [...prevData, JSON.parse(res.data)]);
};
}, []);
return (
<>
<div style={{width: '300px', height: '400px'}}>
<ResponsiveContainer width="100%" height="100%">
<LineChart width={300} height={100} data={data}>
<Line type="monotone" dataKey="pv" stroke="#8884d8" strokeWidth={2} />
</LineChart>
</ResponsiveContainer>
</div>
<div style={{width: '300px', height: '100px', overFlow: 'scroll'}}>
{data.map(item => (
<div>{item.name}</div>
))}
</div>
</>
);
};
4、服务端实现(node.js)
在服务器端,需要使用“text/event-stream”作为响应的Content-Type,并按照SSE的数据格式发送数据。以Spring框架为例,可以使用SseEmitter类来处理SSE事件。SseEmitter允许服务器端以流的形式推送事件给客户端,而不需要客户端不断轮询服务器。
const Koa = require('koa');
const Router = require('koa-router');
const { PassThrough } = require('stream')
//路径管理
const path = require('path');
const static = require('koa-static');
const main = static(path.join(__dirname) + '/www/');
const app = new Koa();
const router = new Router();
app.use(main)
// 发送消息
const sendMessage = async (stream) => {
const data = [
{
name: 'Page A',
uv: 4000,
pv: 2400,
amt: 2400,
},
{
name: 'Page B',
uv: 3000,
pv: 1398,
amt: 2210,
},
{
name: 'Page C',
uv: 2000,
pv: 9800,
amt: 2290,
},
{
name: 'Page D',
uv: 2780,
pv: 3908,
amt: 2000,
},
{
name: 'Page E',
uv: 1890,
pv: 4800,
amt: 2181,
},
{
name: 'Page F',
uv: 2390,
pv: 3800,
amt: 2500,
},
{
name: 'Page G',
uv: 3490,
pv: 4300,
amt: 2100,
},
];
// 循环上面数组: 推送数据、休眠 2 秒
for (const value of data) {
stream.write('data: ' + JSON.stringify(value) + '\n\n');; // 写入数据(推送数据)
await new Promise((resolve) => setTimeout(resolve, 2000));
};
};
// SSE 路由处理
router.get('/api/sse', async (ctx, next) => {
// 设置响应头
ctx.set({
'Content-Type': 'text/event-stream',
'Cache-Control': 'no-cache',
'Connection': 'keep-alive'
});
// 2. 创建流、并作为接口数据进行返回
const stream = new PassThrough();
ctx.body = stream;
ctx.status = 200;
// 3. 推送流数据
sendMessage(stream, ctx);
});
app.use(router.routes()).use(router.allowedMethods());
app.listen(3005, () => {
console.log('Server is running on http://localhost:3005');
});
5、效果展示
(视频无法上传,效果可自行在本地查看!)
6、应用场景
SSE技术特别适用于需要服务器向客户端实时推送数据的场景,例如:
- 实时监控:服务器可以根据设备的状态,实时地将监控数据或报警信息推送给客户端。
- 新闻实时推送:服务器可以根据新闻的更新,实时地将新闻内容或标题推送给客户端。
- 股票价格更新:服务器可以根据股市的变化,实时地将股票价格推送给客户端。
- 车站大屏:服务器根据车次的更新,实时将车次更新在大屏上。