SSE协议的全称是Server-Send Events,专门用于服务端实时推送消息。
SSE是基于HTTP协议,通过长连接的方式持续获取消息。也就是客户端建立TCP链接后,向服务端发起一个HTTP请求,服务端接收到请求后把要返回的内容,按照事件流的方式,不断推送给客户端。跟下载文件一样,所有内容推送完了,连接才关闭。
SSE协议本质上是对HTTP返回的内容进行了约定,客户端按照约定进行解析。
协议的优缺点
SSE协议和websocket协议都可以用作服务端推送,相对于websocket协议的优势有:
- 基于http协议,主流浏览器都支持
- 轻量级协议
- 可以定制消息类型
- 支持超时重连
也有一些不足:
- 服务端单向推送消息,不是全双工可以双向通信
- 协议不支持二进制传输,需要使用方把现在转成二进制格式
协议格式
协议头
SSE协议返回的是事件流,需要指定内容类型。并且,协议是长连接,也要开启长连接和禁止缓存内容。所以要在HTTP header里加上这3个信息:
- Content-Type:text/event-stream
- Connection:keep-alive
- Cache-Control:no-cache
协议内容
协议内容放在http返回的body里,每次返回一个Event信息。每个Event里可以包含5个属性:
- id
- event
- data
- retry
- :(注释消息)
每个属性值占用一行,每行的内容都是由属性名称+属性值组成+换行符,之间用冒号隔开(:)。连个消息之间用额外的换行符区分。
// 这是服务端返回的两条消息
id:1
data:你
data:好,
id:2
event: question
data:你的
data:名字叫什么?
属性介绍
id字段
id用于表示Event的序号,客户端通过序号实现断线重连功能。需要重连的时候,客户端在HTTP的header里加一个Last-Event-ID字段,把最后接收到的id传给服务端。服务端实现了重连功能,就能继续传Last-Event-ID之后的消息给客户端。
event字段
event表示自定义事件类型,客户端通过该字段区分不同消息。
id:2
event: question
data:你的
data:名字叫什么?
data字段
data表示返回的业务数据,如果数据很长可以分成多行返回。
id:1
data:你
data:好,
retry字段
retry表示重连的间隔,以毫秒为单位。
id:1
retry:30000
data:你
data:好,
:(注释消息)
需要返回注释消息的时候,格式是以冒号开头,后面接注释信息
id:1
data:你
data:好,
:这是注释信息
Spring实现SSE接口
spring webmvc已经支持了SSE协议,要返回这是一个简单的SSE接口示例,会返回3条消息:
@GetMapping(value = "/sse")
public SseEmitter sse(HttpServletResponse response) {
response.setCharacterEncoding("UTF-8");
SseEmitter sseEmitter = new SseEmitter();
sseEmitter.onCompletion(() -> System.out.println("complete"));
new Thread(() -> {
for (int i = 1; i <= 3; i++) {
try {
Set<ResponseBodyEmitter.DataWithMediaType> body = SseEmitter.event()
.id(String.valueOf(i))
.name("custom-" + i)
.data("你好:" + i)
.data("我叫AA" + i)
.comment("这是注释内容")
.reconnectTime(5000)
.build();
sseEmitter.send(body);
Thread.sleep(1000);
} catch (Exception e) {
e.printStackTrace();
}
}
// 断开连接
sseEmitter.complete();
}).start();
return sseEmitter;
}
这里做了几件事:
- 通过SseEmitter对象返回事件流,这是时会在响应头里自动加上Content-Type: text/event-stream、Connection: keep-alive信息
- 事件流结束后调用sseEmitter.complete()关闭连接
- 使用onCompletion方法监听连接关闭消息
- 通过HttpServletResponse来设置返回的编码为UTF-8,最终返回的header是Content-Type: text/event-stream;charset=UTF-8