SSE:一种轻量级的长时连接技术

4,104 阅读4分钟

什么是 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、效果展示

image.png (视频无法上传,效果可自行在本地查看!)

6、应用场景

SSE技术特别适用于需要服务器向客户端实时推送数据的场景,例如:

  • 实时监控:服务器可以根据设备的状态,实时地将监控数据或报警信息推送给客户端。
  • 新闻实时推送:服务器可以根据新闻的更新,实时地将新闻内容或标题推送给客户端。
  • 股票价格更新:服务器可以根据股市的变化,实时地将股票价格推送给客户端。
  • 车站大屏:服务器根据车次的更新,实时将车次更新在大屏上。