Springboot集成SSE实现消息推送之单工通信
前言
通常在一些web项目中,会涉及到想客户端推送消息,常见的有Ajax轮询、webSocket,本篇文章主要使用Springboot集成SSE实现向客户端持续推送信息。
SSE简介
服务发送事件SSE(Sever-Sent Event),就是基于 HTTP 的技术,浏览器向服务器发送一个保持长连接HTTP请求,服务器单向地向客户端以流形式持续传输数据 。这样可以节约网络资源,不需要建立新连接。
优点
服务端不需要其他的类库,开发难度较低。
不用每次建立新连接,延迟较低。
数据通过简单且广泛使用的HTTP协议而不是专有协议进行同步。对重新建立连接和事件ID功能的内置支持。
对于利用单向通信的应用程序和服务非常有用。
缺点
客户端越多连接越多,会占用服务器大量内存和连接数。
SSE只支持UTF-8编码,不支持二进制数据。
对最大打开连接数的严格限制可能使事情变得困难,每个浏览器都设置了限制。
SSE是单向的。
Springboot集成SSE简约版
客户端发送请求到服务端,服务端以流的形式不断向客户端推送数据示例,增加帅气值。
xml依赖:
<dependency><groupId>org.springframework.bootgroupId><artifactId>spring-boot-starter-webartifactId>dependency>
html代码:
DOCTYPE html><html><head> <meta charset="UTF-8"> <title>Springboot集成SSE简约版title> <script type="text/javascript"> let source = new EventSource('/get'); source.onmessage = function (event) { console.info(event.data); document.getElementById('text').innerText = event.data }; script>head><body><div id="text">div>body>html>
后端代码:
@RequestMapping(value = "/get", produces = "text/event-stream;charset=UTF-8") public void push(HttpServletResponse response) { response.setContentType("text/event-stream"); response.setCharacterEncoding("utf-8"); int i = 0; while (true) { try { Thread.sleep(1000); PrintWriter pw = response.getWriter(); //注意返回数据必须以data:开头,"\n\n"结尾 pw.write("data:xdm帅气值加" + i + "\n\n"); pw.flush(); //检测异常时断开连接 if (pw.checkError()) { log.error("客户端断开连接"); return; } } catch (Exception e) { e.printStackTrace(); } i++; } }
效果:
Springboot集成SSE升级版
演示SSE的连接建立、接收数据和异常情况监听处理。
注:若浏览器不兼容在页面引入evensource.js。
<script class="lazy" data-src=/eventsource-polyfill.js>script>
客户端代码:
DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <title> Springboot集成SSE升级版title>head><script> let source = null; const clientId = new Date().getTime(); if (!!window.EventSource) { source = new EventSource('/sse/create?clientId=' + clientId); //建立连接 source.onopen = function (event) { setMessageInnerHTML("建立连接" + event); } //接收数据 source.onmessage = function (event) { setMessageInnerHTML(event.data); } //错误监听 source.onerror = function (event) { if (event.readyState === EventSource.CLOSED) { setMessageInnerHTML("连接关闭"); } else { console.log(event); } } } else { setMessageInnerHTML("浏览器不支持SSE"); } window.onbeforeunload = function () { close(); }; // 关闭 function close() { source.close(); const httpRequest = new XMLHttpRequest(); httpRequest.open('GET', '/sse/close/?clientId=' + clientId, true); httpRequest.send(); console.log("close"); } // 显示消息 function setMessageInnerHTML(innerHTML) { document.getElementById('text').innerHTML += innerHTML + '
'; }script><body><button onclick="close()">关闭连接button><div id="text">div>body>html>
服务端代码:
private static Map<String, SseEmitter> cache = new ConcurrentHashMap<>();String clientId;int sseId;@GetMapping("/create")public SseEmitter create(@RequestParam(name = "clientId", required = false) String clientId) { // 设置超时时间,0表示不过期。默认30000毫秒 //可以在客户端一直断网、直接关闭页面但未提醒后端的情况下,服务端在一定时间等待后自动关闭网络连接 SseEmitter sseEmitter = new SseEmitter(0L); // 是否需要给客户端推送ID if (Strings.isBlank(clientId)) { clientId = UUID.randomUUID().toString(); } this.clientId = clientId; cache.put(clientId, sseEmitter); log.info("sse连接,当前客户端:{}", clientId); return sseEmitter;}@Scheduled(cron = "0/3 * * * * ? ")public void pushMessage() { try { sseId++; SseEmitter sseEmitter = cache.get(clientId); sseEmitter.send( SseEmitter .event() .data("帅气值暴增" + sseId) .id("" + sseId) .reconnectTime(3000) ); } catch (Exception e) { log.error(e.getMessage()); sseId--; }}@GetMapping("/close")public void close(String clientId) { SseEmitter sseEmitter = cache.get(clientId); if (sseEmitter != null) { sseEmitter.complete(); cache.remove(clientId); }}
方法参数说明:
SseEmitter .event() .data("帅气值暴增" + sseId) .id("" + sseId) .reconnectTime(3000)
SseEmitter.event()
用来得到一个记录数据的容器。
.data("帅气值暴增" + sseId)
发送给客户端的数据。
.id("" + sseId)
记录发送数据的标识,服务端可以通过HttpServletRequest的请求头中拿到这个id,判断是否中间有误漏发数据。
.reconnectTime(3000)
定义在网络连接断开后,客户端向后端发起重连的时间间隔(以毫秒为单位)。
效果:
参考资料:
https://docs.spring.io/spring-framework/docs/current/reference/html/web.html
来源地址:https://blog.csdn.net/qq_35764295/article/details/127867136
免责声明:
① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。
② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341