SseEmitter简介
Server-Sent Events 是HTTP协议中的一种,Content-Type为text/event-stream,能够保持长连接,简称Sse ,Emitter意为发射器,顾名思义,SseEmitter用于发送Server-Sent Events,一种服务器推送技术。
常见的服务器推送技术:
(1)长轮询:客户端向服务器发起一个长时间的请求,一直保持打开,直到服务器有新内容推送。效率不高但兼容性好。
(2)SSE(Server Sent Events):服务器可以在需要时一直向客户端推送事件,客户端只需要监听一个事件源。兼容性一般。
(3)WebSocket:基于TCP的双向通信,服务器和客户端建立持久连接,允许双向实时消息传输。兼容性差但效率高。
SSE只能发送文本消息,消息内包含:
消息内容 说明 ID 事件的 ID 事件 事件类型 数据 事件数据 重试 事件流的重新连接时间
支持哪些功能?
- 能主动向单个客户端推送消息。与该客户端保持持久连接。通过此连接,服务器可以随时将事件推送给这个客户端。
- 支持推送不同类型的事件。客户端通过事件的名称能区分不同类型的事件,并作出不同的响应。
- 支持推送基本数据类型和POJO对象。服务器可以推送String、int等基本类型,也可以推送任意的Java对象。
- 能主动通知客户端关闭。通过调用complete()或error()方法,服务器可以主动告知客户端连接已关闭。
如何使用
在Spring MVC中可以很容易的实现.仅仅需要返回一个SseEmitter类型的值
- 同步返回一个建立的 SseEmitter 连接给客户端
- 异步线程中进行数据推送
示例代码:
// 设置连接超时时间,也可以使用spring.mvc.async.request-timeout属性来限制异步请求的超时时间。
final SseEmitter emitter = new SseEmitter(600_000L);
// 注册超时回调,超时后触发
emitter.onTimeout(() -> {
logger.info("连接已超时,正准备关闭");
});
// 注册完成回调,调用 emitter.complete() 触发
emitter.onCompletion(() -> {
logger.info("连接已关闭,正准备释放");
});
// 注册异常回调,调用 emitter.completeWithError() 触发
emitter.onError(throwable -> {
logger.error("连接已异常,正准备关闭");
});
//Controller中直接返回SseEmitter,不调用complete()方法,即可保持长链接。
return emitter;
//向客户端发送消息,客户端连接关闭会触发onError和onCompletion回调
emitter.send("客户端接收文本");
//特有数据格式,本质还是个文本
//格式说明: sb.append("id:").append(id).append('\n').append("event:").append(name).append('\n')...;
emitter.send(SseEmitter.event()
.id("消息ID")
.name("消息名称")
.data("消息详情obj")
.data("消息详情obj", MediaType. APPLICATION_JSON)
//更改默认等待时间。服务端可以设置标志位为0,表示连接关闭则立即发起重试,没有等待时间
.reconnectTime("重连等待时间")
.comment("说明字段"))
//关闭连接
//推送数据结束后,不要在 finally 块中调用 emitter.complete() 来关闭连接,
//否则会触发一个很诡异的BUG,如果此时在很短的时间内请求别的接口,可能会收到一个502 bad Gateway 的异常信息,
//原因可参考:https://blog.youkuaiyun.com/sqlxx/article/details/121429478
emitter.complete();
与WebSocket对比
SseEmitter | WebSocket |
---|---|
服务端主动将事件通过 HTTP 响应推送给客户端(只能被动接收) | 双向通信 |
经常建立和关闭连接 | 建立后保持连接不断 |
原生支持的浏览器相对较少,需要Polyfill(一种代码或插件,用于为浏览器提供尚未支持的原生功能) | 大部分浏览器支持 |
文本 | 文本以及二进制数据 |
拓展了解
响应式编程:https://cloud.tencent.com/developer/article/1497796?from_column=20421&from=20421
实现客户端与服务器端双向通信的方式:轮询(polling)、长轮询(long-polling)和iframe流(streaming)
(1)长轮询(long-polling):长轮询是对轮询的改进版。客户端发送HTTP给服务器之后,看有没有新消息,如果没有新消息,就一直等待(而不是一直去请求了)。当有新消息的时候,才会返回给客户端。 优点是对轮询做了优化,时效性也较好。缺点是:保持连接会消耗资源; 服务器没有返回有效数据,程序超时
(2)iframe流(streaming):是在页面中插入一个隐藏的iframe,利用其src属性在服务器和客户端之间创建一条长连接,服务器向iframe传输数据(通常是HTML,内有负责插入信息的javascript),来实时更新页面。
(3)WebSocket:WebSocket协议是基于TCP的一种新的网络协议。它实现了浏览器与服务器全双工(full-duplex)通信——允许服务器主动发送信息给客户端。它将TCP的Socket(套接字)应用在了webpage上。 优点:支持双向通信,实时性更强;可发送二进制文件;非常节省流量。 但也是有缺点的:浏览器支持程度不一致,不支持断开重连
参考文档:
https://juejin.cn/post/7250328942841495613
https://blog.youkuaiyun.com/lilinhai548/article/details/142095127
https://cloud.tencent.com/developer/article/1497804