Server-Send Event
0. 概念
Server-Sent Events(SSE)是一种用于在服务器和客户端之间实现实时单向通信的技术,建立在HTTP协议的基础上,最简单的标志就是其Reponse Header
中的Content-Type
为text/event-stream
,我们熟知的ChatGPT来回答用户的问题使用的就是SSE
该类型的响应弥补了传统的HTTP请求无法达到的服务器主动推送数据,其返回的数据是以数据流的方式进行,同时与客户端所建立的连接为持久连接
1. 特点
- SSE是单向数据通道的,数据源仅能从服务端流向客户端,无法由客户端发送数据到服务端,相比较之下,WebSocket则是全双工通道
- SSE的响应有特定的格式:
data: {text_content}\n\n
,数据结束时必须保证末尾有2个回车,同时数据响应仅支持字符串,也就是纯文本的数据流,每一次发送的信息,由若干个message
组成,每个message
之间用\n\n
分隔,每个message
内部由若干行组成 - SSE如果涉及到二进制数据则需要编码后传送,WebSocket默认支持传送二进制数据
- SSE轻量级,使用简单,而WebSocket协议相对来说比较复杂
- SSE默认支持断线重连,而关于断线重连WebSocket则需要自定义实现
- SSE支持自定义发送的消息类型,再由客户端监听对应的消息类型
- SSE建立长时间的连接,使得服务器可以实时地将数据推送给客户端,而无需客户端频繁地发起请求
- 由于SSE会保持长时间的连接,因此如果是Tomcat的服务器,一个长连接会占用一个线程数,可能会导致资源浪费,可考虑Node这类服务器,来确保所有的连接用的都是一个线程,资源消耗相对少一些
2. 使用方式
Tips:
本文所使用的客户端是支持JavaScript的浏览器
SSE API只支持GET读请求,不支持POST请求
a. 检测是否支持SSE API
目前除了老版本的 IE/Edge,其他浏览器都支持,可通过以下代码进行检查,兼容性也可以通过Mozilla文档进行查看
1 | if ('EventSource' in window) {...} |
b. 初始化EventSource
实例
可通过以下方法初始化EventSource
实例,该实例用于监听SSE连接的消息事件
1 | var source = new EventSource(url); |
c. EventSource
实例的状态
该实例存在一个readyState
属性,该属性只可读,用于判断该EventSource
实例的状态,值为以下:
- 0: 相当于常量
EventSource.CONNECTING
,表示连接还未建立,或者断线正在重连 - 1: 相当于常量
EvnetSource.OPEN
,表示连接已经建立,可以接受数据 - 2: 相当于常量
EventSource.CLOSED
,表示连接已断,且不会重连
d. 事件监听
可以对EventSource
实例进行事件监听,事件监听指的是服务器推送数据到客户端,同时支持对不同的事件进行监听:
连接一旦建立,就会触发open
事件,可以在onopen
属性定义回调函数,可参照以下2种方式:
1 | source.onopen = function (event) { |
默认情况下,服务端返回的都是message
类型的事件,因此可根据以下代码对message
类型的事件进行监听:
1 | source.addEventListener('message', (e) => { |
e. 断开EventSource
连接
可通过source.close()
方法断开EventSource
的连接
f. SSE服务端响应头
服务端发送SSE数据时,必须带有以下响应头,其中Content-Type
的类型必须是text/event-stream
1 | Content-Type: text/event-stream |
g. SSE服务端响应内容
响应内容统一为以下格式,其中需要注意的是,响应内容的最后一行必须以2个回车符号\n
表示
[类型]: value
,其中类型包含以下四种类型:
- data: 表示该行为数据行,其内容为要返回的数据
- event: 表示该响应内容的事件类型,可自定义事件类型,默认情况下该类型是
message
- id: 表示事件ID,可以简单地理解成是数据的编号
- retry: 用于指定浏览器重新发起连接的时间间隔,两种情况会导致浏览器重新发起连接:一种是时间间隔到期,二是由于网络错误等原因,导致连接出错
h. 注释
服务器可以通过发送以下格式的数据来表示注释,可以通过间断性地发送注释来确保连接不中断,注释一般是冒号开头的行:
1 | : This is a comment |
i. EventSource
增强
由于原版的EventSource API
不支持POST方法,默认发起的是GET方法,因此建议使用基于Fetch API
开源的增强包: @microsoft/fetch-event-source
来更好地解决以上问题
3. 样例
以下案例采用的是Python
语言 + Flask
框架 + HTML
进行打样,其中,页面会初始化EventSource
实例,并由后端提供一个事件推送API,该API会间隔500毫秒轮换推送一个receiveTextMessage
和一个receiveJsonMessage
事件,其中receiveTextMessage
事件负责推送带有字符串的内容,receiveJsonMessage
事件负责推送带有JSON的内容,该接口一共会推送20个事件,如下所示:
- index.html
1 |
|
- app.py
1 | import json |
页面输出效果如下所示: