Server Send Event 是啥

285 阅读5分钟

介绍

最近刚好项目(生成式AI场景)中使用到了 SSE,就来梳理下SSE相关知识

我们知道,HHTP协议无法做到服务器主动推送消息,但是可以通过服务器向客户端声明表示接下来要发送的是流信息

客户端不会关闭连接,会一直等着服务器发过来的新数据,视频播放即是如此。本质上就是以流信息的方式完成一次用时很长的下载

SSE(Server Send Event)实时通信的服务器推送机制 (服务器向客户端实时推送数据)GitHub源码

即客户端从服务端订阅一条“流”,之后服务端可以发送消息给客户端直到服务端或者客户端关闭该“流”,所以 EventSource 也叫作 SSE(server-sent-event)

EventSource

是HTML5中的一项API,用于在客户端和服务器之间建立持久的、单向的通信连接。它基于HTTP协议,通过服务器推送的方式向客户端发送实时事件通知。客户端通过添加事件侦听器来捕获事件并执行相应的操作。

image.png

主要特点

  1. 简单易用:基于文本的数据格式,使得数据的发送和解析都相对简单
  2. 单向通信:支持服务器向客户端的单向通信,服务器可以主动推送数据给客户端
  3. 实时性:建立长时间连接,服务器可实时将数据推送给客户端,无需客户端频繁发起请求

兼容性

除了IE和低版本主流浏览器,市面上大多数浏览器都支持SSE

caniuse.com/eventsource

image.png

对比Websocket

SSE和WebSocket都是建立浏览器和服务器之间的通信渠道,然后服务器向浏览器推送消息

SSEWebSockets
协议基于HTTP,使用标准HTTP连接基于TCP协议
通信方式单向通信(stream数据本质上就是下载)全双工通信
数据格式文本(UTF-8编码)文本或二进制
重连机制自动重连手动实现重连机制
实时性高(适合频繁更新的场景)非常高(适合高度交互的实时应用)
浏览器支持良好(大多数现代浏览器支持)非常好(几乎所有现代浏览器支持)
适用场景实时通知、新闻feed、股票价格等需要从服务器推送到客户端的场景在线游戏、聊天应用、实时交互应用
复杂性较低,易于实现和维护较高,需要处理连接的建立、维护和断开
兼容性和可用性更容易通过各种中间件和防火墙可能需要配置服务器和网络设备以支持WebSocket
服务器负载适合较低频率的数据更新适合高频率消息和高度交互的场景

对比HTTP的长连接PUSH

原理差不多,不过SSE已经写入HTML5标准,浏览器原生支持

长连接Push和SSE都可以实现服务器向客户端的实时推送,但SSE在实现上更简单,而且更适合于需要频繁发送数据的应用。

@microsoft/fetch-event-source

EventSource兼容性好,有很多优点。但也有无法解决的问题

  1. EventSource发出的请求默认GET请求,大部分浏览器GET请求有URL长度限制
  2. 无法传入自定义请求头
  3. 无法控制重试策略

微软有一个封装好的sse库较好解决了这2个问题:@microsoft/fetch-event-source

"@microsoft/fetch-event-source": "^2.0.1",

为了解决以上问题我们基于EventSource进行封装,可以实现如下功能

  • 自定义请求头等其他信息
  • 发送POST请求
  • 控制重试策略。增加超时报错
  • 对于收到消息的监听做封装处理
  • 主动关闭连接

设计

客户端和服务端交互流程图如下所示:

image.png

修改request header:

Content-Type: text/event-stream

查看掘金侧边栏豆包问答,它回答一个问题不是一次性给我们全部,而是一部分一部分加载回答,这也是通过SSE实现通信

2024-08-06 18.58.56.gif

SSE类

我们基于@microsoft/fetch-event-source库封装SSE类:

    var eventSourceFetcher = require('@microsoft/fetch-event-source');
    class EventStream {
        constructor(endpoint: string) {
            this.endpoint = endpoint;
        }
        retrieveData(fetchParams = {}, callback) {
            if (!this.endpoint) {
                return;
            }
            const defaultRequestData = {
                request_stream: 1
            };
            const requestData = Object.assign({}, defaultRequestData, fetchParams);
            eventSourceFetcher(this.endpoint, {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/json'
                },
                openWhenHidden: true,
                body: JSON.stringify(requestData), 
                async onopen(serverResponse) {
                    if (serverResponse.ok && serverResponse.status === 200 && serverResponse.headers.get('content-type')?.includes('text/event-stream')) {
                        console.log("Successfully connected");
                    } else {
                        console.log('Request error');
                    }
                },
                async onmessage(messageEvent) {
                    try {
                        const parsedData = JSON.parse(messageEvent.data);
                        console.log('Received data', parsedData);
                        callback(parsedData)
                        if (parsedData.isEnd) {
                            return;
                        }
                    } catch (parsingError) {
                        console.log('Error parsing data', parsingError)
                    }
                },
                onerror(error) {
                    console.log('Error', error);
                },
                onclose() {
                    console.log('Connection closed');
                }
            });
        }
    }
    exports.EventStream = EventStream;

使用

将携带参数和请求回调一起传入:

    function dataCallback (receivedData) {
       console.log('Received stream data', receivedData);
    };
    const eventStreamInstance = new EventStream('/api/info/sse');
    eventStreamInstance.retrieveData(params, dataCallback);

风险

  1. 如果不使用HTTP/2会受到最大打开连接数的限制,限制最大连接数为6(HTTP/2默认值是100)

image.png

  1. eventSource 默认只能支持get请求:使用@microsoft/fetch-event-source 将sse转为post

image.png

  1. 首次请求事件间距过长 或长时间请求不到超时

stackoverflow.com/questions/1…

  1. MS(Microsoft)浏览器至今不支持SSE?

有很多sse polyfills可以使用

Internet Explorer不支持Server-Sent Events (SSE);但是Edge(基于Chromium)支持SS的

  1. SSE只能接收文本数据吗?

只支持utf8编码,binary数据可以先base64

总结

image.png

参考