东莞网站建设都用哪个好,100%提现赚钱游戏,南阳市建网站公,百度收录网站要多【SpringBoot WEB系列】SSE 服务器发送事件详解 SSE 全称Server Sent Event#xff0c;直译一下就是服务器发送事件#xff0c;一般的项目开发中#xff0c;用到的机会不多#xff0c;可能很多小伙伴不太清楚这个东西#xff0c;到底是干啥的#xff0c;有啥用
本文主要… 【SpringBoot WEB系列】SSE 服务器发送事件详解 SSE 全称Server Sent Event直译一下就是服务器发送事件一般的项目开发中用到的机会不多可能很多小伙伴不太清楚这个东西到底是干啥的有啥用
本文主要知识点如下
SSE 扫盲应用场景分析借助异步请求实现 sse 功能加深概念理解使用SseEmitter实现一个简单的推送示例 I. SSE 扫盲 对于 sse 基础概念比较清楚的可以跳过本节 1. 概念介绍
sse(Server Sent Event)直译为服务器发送事件顾名思义也就是客户端可以获取到服务器发送的事件
我们常见的 http 交互方式是客户端发起请求服务端响应然后一次请求完毕但是在 sse 的场景下客户端发起请求连接一直保持服务端有数据就可以返回数据给客户端这个返回可以是多次间隔的方式
2. 特点分析
SSE 最大的特点可以简单规划为两个
长连接服务端可以向客户端推送信息
了解 websocket 的小伙伴可能也知道它也是长连接可以推送信息但是它们有一个明显的区别
sse 是单通道只能服务端向客户端发消息而 webscoket 是双通道
那么为什么有了 webscoket 还要搞出一个 sse 呢既然存在必然有着它的优越之处
ssewebsockethttp 协议独立的 websocket 协议轻量使用简单相对复杂默认支持断线重连需要自己实现断线重连文本传输二进制传输支持自定义发送的消息类型-
3. 应用场景
从 sse 的特点出发我们可以大致的判断出它的应用场景需要轮询获取服务端最新数据的 case 下多半是可以用它的
比如显示当前网站在线的实时人数法币汇率显示当前实时汇率电商大促的实时成交额等等…
II. 手动实现 sse 功能
sse 本身是有自己的一套玩法的后面会进行说明这一小节则主要针对 sse 的两个特点长连接 后端推送数据如果让我们自己来实现这样的一个接口可以怎么做
1. 项目创建
借助 SpringBoot 2.2.1.RELEASE来创建一个用于演示的工程项目核心的 xml 依赖如下
parentgroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-parent/artifactIdversion2.2.1.RELEASE/versionrelativePath/ !-- lookup parent from repository --
/parentpropertiesproject.build.sourceEncodingUTF-8/project.build.sourceEncodingproject.reporting.outputEncodingUTF-8/project.reporting.outputEncodingjava.version1.8/java.version
/propertiesdependenciesdependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-web/artifactId/dependency
/dependenciesbuildpluginManagementpluginsplugingroupIdorg.springframework.boot/groupIdartifactIdspring-boot-maven-plugin/artifactId/plugin/plugins/pluginManagement
/build
repositoriesrepositoryidspring-snapshots/idnameSpring Snapshots/nameurlhttps://repo.spring.io/libs-snapshot-local/urlsnapshotsenabledtrue/enabled/snapshots/repositoryrepositoryidspring-milestones/idnameSpring Milestones/nameurlhttps://repo.spring.io/libs-milestone-local/urlsnapshotsenabledfalse/enabled/snapshots/repositoryrepositoryidspring-releases/idnameSpring Releases/nameurlhttps://repo.spring.io/libs-release-local/urlsnapshotsenabledfalse/enabled/snapshots/repository
/repositories2. 功能实现
在 Http1.1 支持了长连接请求头添加一个Connection: keep-alive即可
在这里我们借助异步请求来实现 sse 功能至于什么是异步请求推荐查看博文: 【WEB 系列】异步请求知识点与使用姿势小结
因为后端可以不定时返回数据所以我们需要注意的就是需要保持连接不要返回一次数据之后就断开了其次就是需要设置请求头Content-Type: text/event-stream;charsetUTF-8 如果不是流的话会怎样
// 新建一个容器保存连接用于输出返回
private MapString, PrintWriter responseMap new ConcurrentHashMap();// 发送数据给客户端
private void writeData(String id, String msg, boolean over) throws IOException {PrintWriter writer responseMap.get(id);if (writer null) {return;}writer.println(msg);writer.flush();if (over) {responseMap.remove(id);}
}// 推送
ResponseBody
GetMapping(path subscribe)
public WebAsyncTaskVoid subscribe(String id, HttpServletResponse response) {CallableVoid callable () - {response.setHeader(Content-Type, text/event-stream;charsetUTF-8);responseMap.put(id, response.getWriter());writeData(id, 订阅成功, false);while (true) {Thread.sleep(1000);if (!responseMap.containsKey(id)) {break;}}return null;};// 采用WebAsyncTask 返回 这样可以处理超时和错误 同时也可以指定使用的Excutor名称WebAsyncTaskVoid webAsyncTask new WebAsyncTask(30000, callable);// 注意onCompletion表示完成不管你是否超时、是否抛出异常这个函数都会执行的webAsyncTask.onCompletion(() - System.out.println(程序[正常执行]完成的回调));// 这两个返回的内容最终都会放进response里面去webAsyncTask.onTimeout(() - {responseMap.remove(id);System.out.println(超时了!!!);return null;});// 备注这个是Spring5新增的webAsyncTask.onError(() - {System.out.println(出现异常!!!);return null;});return webAsyncTask;
}看一下上面的实现基本上还是异步请求的那一套逻辑请仔细看一下callable中的逻辑有一个 while 循环来保证长连接不中断
接下来我们新增两个接口用来模拟后端给客户端发送消息关闭连接的场景
ResponseBody
GetMapping(path push)
public String pushData(String id, String content) throws IOException {writeData(id, content, false);return over!;
}ResponseBody
GetMapping(path over)
public String over(String id) throws IOException {writeData(id, over, true);return over!;
}我们简单的来演示下操作过程 III. SseEmitter
上面只是简单实现了 sse 的长连接 后端推送消息但是与标准的 SSE 还是有区别的sse 有自己的规范而我们上面的实现实际上并没有管这个导致的问题是前端按照 sse 的玩法来请求数据可能并不能正常工作
1. sse 规范
在 html5 的定义中服务端 sse一般需要遵循以下要求
请求头
开启长连接 流方式传递
Content-Type: text/event-stream;charsetUTF-8
Cache-Control: no-cache
Connection: keep-alive数据格式
服务端发送的消息由 message 组成其格式如下:
field:value\n\n其中 field 有五种可能
空: 即以:开头表示注释可以理解为服务端向客户端发送的心跳确保连接不中断data数据event: 事件默认值id: 数据标识符用 id 字段表示相当于每一条数据的编号retry: 重连时间
2. 实现
SpringBoot 利用 SseEmitter 来支持 sse可以说非常简单了直接返回SseEmitter对象即可重写一下上面的逻辑
RestController
RequestMapping(path sse)
public class SseRest {private static MapString, SseEmitter sseCache new ConcurrentHashMap();GetMapping(path subscribe)public SseEmitter push(String id) {// 超时时间设置为1小时SseEmitter sseEmitter new SseEmitter(3600_000L);sseCache.put(id, sseEmitter);sseEmitter.onTimeout(() - sseCache.remove(id));sseEmitter.onCompletion(() - System.out.println(完成));return sseEmitter;}GetMapping(path push)public String push(String id, String content) throws IOException {SseEmitter sseEmitter sseCache.get(id);if (sseEmitter ! null) {sseEmitter.send(content);}return over;}GetMapping(path over)public String over(String id) {SseEmitter sseEmitter sseCache.get(id);if (sseEmitter ! null) {sseEmitter.complete();sseCache.remove(id);}return over;}
}上面的实现用到了 SseEmitter 的几个方法解释如下
send(): 发送数据如果传入的是一个非SseEventBuilder对象那么传递参数会被封装到 data 中complete(): 表示执行完毕会断开连接onTimeout(): 超时回调触发onCompletion(): 结束之后的回调触发
同样演示一下访问请求
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-W3mifby7-1585829791863)(https://spring.hhui.top/spring-blog/imgs/200401/01.gif)]
上图总的效果和前面的效果差不多而且输出还待上了前缀接下来我们写一个简单的 html 消费端用来演示一下完整的 sse 的更多特性
!doctype html
html langen
headtitleSse测试文档/title
/head
body
divsse测试/div
div idresult/div
/body
/html
scriptvar source new EventSource(http://localhost:8080/sse/subscribe?idyihuihui);source.onmessage function (event) {text document.getElementById(result).innerText;text \n event.data;document.getElementById(result).innerText text;};!-- 添加一个开启回调 --source.onopen function (event) {text document.getElementById(result).innerText;text \n 开启: ;console.log(event);document.getElementById(result).innerText text;};
/script将上面的 html 文件放在项目的resources/static目录下然后修改一下前面的SseRest
Controller
RequestMapping(path sse)
public class SseRest {GetMapping(path )public String index() {return index.html;}ResponseBodyGetMapping(path subscribe, produces {MediaType.TEXT_EVENT_STREAM_VALUE})public SseEmitter push(String id) {// 超时时间设置为3s用于演示客户端自动重连SseEmitter sseEmitter new SseEmitter(1_000L);// 设置前端的重试时间为1ssseEmitter.send(SseEmitter.event().reconnectTime(1000).data(连接成功));sseCache.put(id, sseEmitter);System.out.println(add id);sseEmitter.onTimeout(() - {System.out.println(id 超时);sseCache.remove(id);});sseEmitter.onCompletion(() - System.out.println(完成));return sseEmitter;}
}我们上面超时时间设置的比较短用来测试下客户端的自动重连如下开启的日志不断增加 其次将 SseEmitter 的超时时间设长一点再试一下数据推送功能
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Bl5EdI8i-1585829791875)(https://spring.hhui.top/spring-blog/imgs/200401/03.gif)]
请注意上面的演示当后端结束了长连接之后客户端会自动重新再次连接不用写额外的重试逻辑了就这么神奇
3. 小结
本篇文章介绍了 SSE 的相关知识点并对比 websocket 给出了 sse 的优点至于啥优点请往上翻
请注意本文虽然介绍了两种 sse 的方式第一种借助异步请求来实现如果需要完成 sse 的规范要求需要自己做一些适配如果需要了解 sse 底层实现原理的话可以参考一下在实际的业务开发中推荐使用SseEmitter
IV. 其他
0. 项目
系列博文
200329-SpringBoot 系列教程 web 篇之异步请求知识点与使用姿势小结200105-SpringBoot 系列教程 web 篇之自定义返回 Http-Code 的 n 种姿势191222-SpringBoot 系列教程 web 篇之自定义请求匹配条件 RequestCondition191206-SpringBoot 系列教程 web 篇 Listener 四种注册姿势191122-SpringBoot 系列教程 web 篇 Servlet 注册的四种姿势191120-SpringBoot 系列教程 Web 篇之开启 GZIP 数据压缩191018-SpringBoot 系列教程 web 篇之过滤器 Filter 使用指南扩展篇191016-SpringBoot 系列教程 web 篇之过滤器 Filter 使用指南191012-SpringBoot 系列教程 web 篇之自定义异常处理 HandlerExceptionResolver191010-SpringBoot 系列教程 web 篇之全局异常处理190930-SpringBoot 系列教程 web 篇之 404、500 异常页面配置190929-SpringBoot 系列教程 web 篇之重定向190913-SpringBoot 系列教程 web 篇之返回文本、网页、图片的操作姿势190905-SpringBoot 系列教程 web 篇之中文乱码问题解决190831-SpringBoot 系列教程 web 篇之如何自定义参数解析器190828-SpringBoot 系列教程 web 篇之 Post 请求参数解析姿势汇总190824-SpringBoot 系列教程 web 篇之 Get 请求参数解析姿势汇总190822-SpringBoot 系列教程 web 篇之 Beetl 环境搭建190820-SpringBoot 系列教程 web 篇之 Thymeleaf 环境搭建190816-SpringBoot 系列教程 web 篇之 Freemaker 环境搭建190421-SpringBoot 高级篇 WEB 之 websocket 的使用说明190327-Spring-RestTemplate 之 urlencode 参数解析异常全程分析190317-Spring MVC 之基于 java config 无 xml 配置的 web 应用构建190316-Spring MVC 之基于 xml 配置的 web 应用构建190213-SpringBoot 文件上传异常之提示 The temporary upload location xxx is not valid
源码
工程https://github.com/liuyueyi/spring-boot-demo项目源码: https://github.com/liuyueyi/spring-boot-demo/blob/master/spring-boot/220-web-sse
1. 一灰灰 Blog
尽信书则不如以上内容纯属一家之言因个人能力有限难免有疏漏和错误之处如发现 bug 或者有更好的建议欢迎批评指正不吝感激
下面一灰灰的个人博客记录所有学习和工作中的博文欢迎大家前去逛逛
一灰灰 Blog 个人博客 https://blog.hhui.top一灰灰 Blog-Spring 专题博客 http://spring.hhui.top
文章转载自: http://www.morning.hfbtt.cn.gov.cn.hfbtt.cn http://www.morning.lkbyq.cn.gov.cn.lkbyq.cn http://www.morning.jrsgs.cn.gov.cn.jrsgs.cn http://www.morning.wtnwf.cn.gov.cn.wtnwf.cn http://www.morning.lkcqz.cn.gov.cn.lkcqz.cn http://www.morning.ftnhr.cn.gov.cn.ftnhr.cn http://www.morning.krnzm.cn.gov.cn.krnzm.cn http://www.morning.ntwxt.cn.gov.cn.ntwxt.cn http://www.morning.rszbj.cn.gov.cn.rszbj.cn http://www.morning.jhrqn.cn.gov.cn.jhrqn.cn http://www.morning.rtqyy.cn.gov.cn.rtqyy.cn http://www.morning.nshhf.cn.gov.cn.nshhf.cn http://www.morning.nlrp.cn.gov.cn.nlrp.cn http://www.morning.lprfk.cn.gov.cn.lprfk.cn http://www.morning.kpbgvaf.cn.gov.cn.kpbgvaf.cn http://www.morning.ctfwl.cn.gov.cn.ctfwl.cn http://www.morning.hpggl.cn.gov.cn.hpggl.cn http://www.morning.mzcsp.cn.gov.cn.mzcsp.cn http://www.morning.sgmis.com.gov.cn.sgmis.com http://www.morning.yrcxg.cn.gov.cn.yrcxg.cn http://www.morning.nbiotank.com.gov.cn.nbiotank.com http://www.morning.vuref.cn.gov.cn.vuref.cn http://www.morning.rfpxq.cn.gov.cn.rfpxq.cn http://www.morning.yhwmg.cn.gov.cn.yhwmg.cn http://www.morning.kqpsj.cn.gov.cn.kqpsj.cn http://www.morning.ybgcn.cn.gov.cn.ybgcn.cn http://www.morning.zzfqn.cn.gov.cn.zzfqn.cn http://www.morning.dwrbn.cn.gov.cn.dwrbn.cn http://www.morning.flxqm.cn.gov.cn.flxqm.cn http://www.morning.rfzbm.cn.gov.cn.rfzbm.cn http://www.morning.tqygx.cn.gov.cn.tqygx.cn http://www.morning.wjplr.cn.gov.cn.wjplr.cn http://www.morning.hmxb.cn.gov.cn.hmxb.cn http://www.morning.nhlnh.cn.gov.cn.nhlnh.cn http://www.morning.bfybb.cn.gov.cn.bfybb.cn http://www.morning.cbndj.cn.gov.cn.cbndj.cn http://www.morning.rgzc.cn.gov.cn.rgzc.cn http://www.morning.gtbjf.cn.gov.cn.gtbjf.cn http://www.morning.kwfnt.cn.gov.cn.kwfnt.cn http://www.morning.rbzht.cn.gov.cn.rbzht.cn http://www.morning.bpncd.cn.gov.cn.bpncd.cn http://www.morning.ctbr.cn.gov.cn.ctbr.cn http://www.morning.stcds.cn.gov.cn.stcds.cn http://www.morning.mdfxn.cn.gov.cn.mdfxn.cn http://www.morning.rrbhy.cn.gov.cn.rrbhy.cn http://www.morning.zsrjn.cn.gov.cn.zsrjn.cn http://www.morning.wnkqt.cn.gov.cn.wnkqt.cn http://www.morning.xwlmg.cn.gov.cn.xwlmg.cn http://www.morning.qdrrh.cn.gov.cn.qdrrh.cn http://www.morning.pabxcp.com.gov.cn.pabxcp.com http://www.morning.dnqlba.cn.gov.cn.dnqlba.cn http://www.morning.rjjjk.cn.gov.cn.rjjjk.cn http://www.morning.xtgzp.cn.gov.cn.xtgzp.cn http://www.morning.fypgl.cn.gov.cn.fypgl.cn http://www.morning.zfhzx.cn.gov.cn.zfhzx.cn http://www.morning.zcxjg.cn.gov.cn.zcxjg.cn http://www.morning.rbnj.cn.gov.cn.rbnj.cn http://www.morning.hrzhg.cn.gov.cn.hrzhg.cn http://www.morning.rnrwq.cn.gov.cn.rnrwq.cn http://www.morning.dbylp.cn.gov.cn.dbylp.cn http://www.morning.mfct.cn.gov.cn.mfct.cn http://www.morning.hwycs.cn.gov.cn.hwycs.cn http://www.morning.bbyqz.cn.gov.cn.bbyqz.cn http://www.morning.xqcst.cn.gov.cn.xqcst.cn http://www.morning.qxnlc.cn.gov.cn.qxnlc.cn http://www.morning.kwz6232.cn.gov.cn.kwz6232.cn http://www.morning.bbyqz.cn.gov.cn.bbyqz.cn http://www.morning.znrgq.cn.gov.cn.znrgq.cn http://www.morning.zbqry.cn.gov.cn.zbqry.cn http://www.morning.brtxg.cn.gov.cn.brtxg.cn http://www.morning.yrnyz.cn.gov.cn.yrnyz.cn http://www.morning.bmts.cn.gov.cn.bmts.cn http://www.morning.dsxgc.cn.gov.cn.dsxgc.cn http://www.morning.jqtb.cn.gov.cn.jqtb.cn http://www.morning.pypbz.cn.gov.cn.pypbz.cn http://www.morning.wrtpk.cn.gov.cn.wrtpk.cn http://www.morning.ysgnb.cn.gov.cn.ysgnb.cn http://www.morning.tqdqc.cn.gov.cn.tqdqc.cn http://www.morning.tbjb.cn.gov.cn.tbjb.cn http://www.morning.pphgl.cn.gov.cn.pphgl.cn