SSE 推送技术

1、简介

Server-Sent Events(SSE)技术,它是一种用于实现服务器向客户端实时单向推送数据的Web技术。
SSE基于HTTP协议,允许服务器将数据以事件流(Event Stream)的形式发送给客户端。客户端通过建立持久的HTTP连接,并监听事件流,可以实时接收服务器推送的数据。
之前分享了一篇关于websocket技术的文章。本篇算是之前内容的一个补充。

官网摘要:

2、SSE和WebSocket的区别

WebSocket是另一种用于实现实时双向通信的Web技术。

  • 数据推送方面

    • SSE 是服务端像客户端的单向通信的技术。

    • WebSocket是双向通讯的技术

  • 协议方面

    • SSE是基于HTTP协议的长连接,超时后可以自动重连

    • WebSocket是基于ws协议的,建立双向连接实现通讯的

3、SSE的使用

SSE的使用无需引入特别的包,因为是一个Web技术,只要应为web对应的依赖即可。SSE的客户端是SseEmitter

@RestController
@RequestMapping("/foo")
public class FooController {Map<Integer,SseEmitter> map = Maps.newConcurrentMap();Map<Integer,SseEmitter> doneMap = Maps.newConcurrentMap();String curentContext = "";@GetMapping(value = "/sse", produces = {MediaType.TEXT_EVENT_STREAM_VALUE})public SseEmitter sseEmitter(HttpServletRequest request) throws IOException {String messageId = request.getHeader("Last-Event-ID");System.out.println("Last-Event-ID 重新连接:" + messageId);Integer sseEmitterId = RandomUtils.nextInt();SseEmitter sseEmitter = new SseEmitter(15000L);System.out.println("sseEmitter 建立连接... sseEmitterId=" + sseEmitterId);map.put(sseEmitterId, sseEmitter);if (StringUtils.isNotBlank(messageId)) {if (!doneMap.containsKey(Integer.valueOf(messageId))) {sseEmitter.send(SseEmitter.event().id(messageId).data("来自客户端【" + messageId + "】补发的信息:" + curentContext));}}sseEmitter.onCompletion(() -> {System.out.println("sseEmitter 结束... sseEmitterId=" + sseEmitterId);map.remove(sseEmitterId);});return sseEmitter;}@GetMapping("/sseSend")public String sseSend() {System.out.println("获取的sseEmitter客户端:" + JSON.toJSONString(map));curentContext = "测试SSE" + DateFormatUtils.format(new Date(), "yyyy-MM-dd HH:mm:ss");if (!map.isEmpty()) {doneMap.clear();map.forEach((key, value) -> {try {value.send(SseEmitter.event().id(String.valueOf(key)).data("来自客户端【" + key + "】的信息:" + curentContext));doneMap.put(key, value);// value.send("来自客户端【" + key + "】的信息-----------:" + j);Thread.sleep(1000);} catch (Exception e) {throw new RuntimeException(e);}});}return "发送成功!";}@GetMapping("/closeWindow")public void test05() {System.out.println("closeWindow 窗口被关闭了....");}
}
3.1 订阅方法说明(/foo/sse)

/foo/sse 为订阅的方法。客户端打开页面,订阅该接口。返回的SseEmitter 为当前页面专属的连接,可以通过该连接推送消息。

注意事项:

  • 订阅的返回值必须是SseEmitter ,返回的数据类型为事件流。执行返回类型的的话需要配置produces = {MediaType.TEXT_EVENT_STREAM_VALUE} 。也可以不配置,请求会自动匹配。

  • 消息的发送,必须通过返回的SseEmitter,调用send()方法。由于需要实时推送,所以需要将创建的SseEmitter 缓存起来,随时推送消息。

  • SseEmitter 空参构造函数默认的超时时间为60s,也可以通过构造参数设置超时时间。案例中超时时间15s。如果设置成0,则表示永不超时。

  • Header中Last-Event-ID 参数为当前连接最新推送消息的ID,该ID可以自定义。消息推送之后,客户端重连之后,Header中会自动携带此参数(Last-Event-ID)。

  • SseEmitter 连接可以注册onCompletion【关闭】,onTimeOut【超时】,onError【错误】事件的回调。

3.2 模拟消息推送(/foo/sseSend)

/foo/sseSend 模拟消息推送的方法。获取创建的SseEmitter 连接,然后逐个推送消息。

注意事项:

  • map里面存放客户端的ID和连接。
  • 因为SSE连接会超时,超时的连接关闭之后会通过回调删除连接,所以重新连接的连接不会受到消息的推送。所以使用doneMap记录已经推送的客户端。自动连接的新连接补发消息。
3.3 页面关闭事件(/foo/closeWindow)

/foo/closeWindow 是页面被关闭时的请求连接。正常的逻辑里面,应该删除服务端保存的连接。案例中没有去实现,因为页面关闭有兼容性问题。只做演示。

  • 页面关闭的事件有兼容性问题,不能保证一定会触发

  • 页面关闭后,推送消息的连接应该被清除,否则容易引起OOM

  • 设置超时时间,通过回调可以避免这种问题。但是如果设置成永不超时,则会必须处理页面被关闭后连接的清除。

  • 关闭连接也可以使用客户端close方法直接关闭,但是如果发型消息的话会报错

    Caused by: java.io.IOException: 你的主机中的软件中止了一个已建立的连接。at sun.nio.ch.SocketDispatcher.write0(Native Method) ~[na:1.8.0_202]at sun.nio.ch.SocketDispatcher.write(SocketDispatcher.java:51) ~[na:1.8.0_202]at sun.nio.ch.IOUtil.writeFromNativeBuffer(IOUtil.java:93) ~[na:1.8.0_202]at sun.nio.ch.IOUtil.write(IOUtil.java:65) ~[na:1.8.0_202]at sun.nio.ch.SocketChannelImpl.write(SocketChannelImpl.java:471) ~[na:1.8.0_202]at org.apache.tomcat.util.net.NioChannel.write(NioChannel.java:135) ~[tomcat-embed-core-9.0.65.jar:9.0.65]at org.apache.tomcat.util.net.NioEndpoint$NioSocketWrapper.doWrite(NioEndpoint.java:1424) ~[tomcat-embed-core-9.0.65.jar:9.0.65]at org.apache.tomcat.util.net.SocketWrapperBase.doWrite(SocketWrapperBase.java:768) ~[tomcat-embed-core-9.0.65.jar:9.0.65]at org.apache.tomcat.util.net.SocketWrapperBase.flushBlocking(SocketWrapperBase.java:732) ~[tomcat-embed-core-9.0.65.jar:9.0.65]at org.apache.tomcat.util.net.SocketWrapperBase.flush(SocketWrapperBase.java:716) ~[tomcat-embed-core-9.0.65.jar:9.0.65]at org.apache.coyote.http11.Http11OutputBuffer$SocketOutputBuffer.flush(Http11OutputBuffer.java:573) ~[tomcat-embed-core-9.0.65.jar:9.0.65]at org.apache.coyote.http11.filters.ChunkedOutputFilter.flush(ChunkedOutputFilter.java:157) ~[tomcat-embed-core-9.0.65.jar:9.0.65]at org.apache.coyote.http11.Http11OutputBuffer.flush(Http11OutputBuffer.java:221) ~[tomcat-embed-core-9.0.65.jar:9.0.65]at org.apache.coyote.http11.Http11Processor.flush(Http11Processor.java:1255) [tomcat-embed-core-9.0.65.jar:9.0.65]at org.apache.coyote.AbstractProcessor.action(AbstractProcessor.java:402) ~[tomcat-embed-core-9.0.65.jar:9.0.65]at org.apache.coyote.Response.action(Response.java:209) ~[tomcat-embed-core-9.0.65.jar:9.0.65]at org.apache.catalina.connector.OutputBuffer.doFlush(OutputBuffer.java:306) ~[tomcat-embed-core-9.0.65.jar:9.0.65]... 67 common frames omitted
    

4、客户端的使用

不需要引入任何js,直接使用EventSource 建立连接。

案例:

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>测试SSE</title>
</head>
<body><h1>测试SSE</h1><div id="stock-price"></div><div id="closeConnect">关闭连接</div>
</body>
<script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.min.js"></script>
<script>let eventSource;function init() {eventSource = new EventSource('/foo/sse');eventSource.onmessage = function (event) {console.info(event);document.getElementById('stock-price').innerHTML = event.data;};eventSource.onerror = function (event) {console.info(event.data + "::::exception");// if (event.target.readyState === EventSource.CLOSED) {//     init();// }};eventSource.addEventListener('test', e => {console.log(`message-data: ${e.data}`);}, false);}init();window.onbeforeunload  = function(e) {$.get("/foo/closeWindow", {});};$("#closeConnect").click(function(){console.info("close connection");eventSource.close();});
</script>
</html>
4.1 建立连接

new EventSource('/foo/sse') 建立连接/订阅消息,页面打开,方法执行会根据订阅的路径请求服务端获取连接。

4.2 监听消息

  • eventSource.onmessage 监听推送的消息,event.data直接可以获取推送的消息。

  • eventSource.onerror 监听异常的消息

  • eventSource.close() 关闭连接

  • eventSource.addEventListener 监听自定义时间,服务端通过SseEmitter.event().name(xxx)来设置事件的名称。

4.3 页面关闭的事件

window.onbeforeunload 监听页面的关闭,但是存在兼容性问题,或者页面异常的关闭都不会触发该方法。所以此方法不可靠。

5、案例演示

5.1 客户端页面

页面的跳转,订阅/foo/sse接口,等待消息推送。

5.2 模拟消息的推送

模拟推送消息/foo/sseSend

6、小结

  • SSE的使用要注意过期时间的设置,使用了过期时间,就要考虑客户端重连的消息的丢失问题。
  • 使用了永不过期就要考虑防止客户端连接过多造成的OOM

7、参考文档

https://zh.javascript.info/server-sent-events

https://javascript.info/server-sent-events

https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.xdnf.cn/news/143552.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

秋招在线人才测评考什么内容?

又是一年招聘季&#xff0c;各大高校都会组织校园招聘&#xff0c;这次我们就来了解一下秋季校园招聘究竟考什么。近些年来校园秋招已经广泛采用在线测评&#xff0c;尤其各行业龙头大厂们&#xff0c;网申、在线测评、小组无领导讨论&#xff0c;一面二面......各类纷杂的面试…

SpringMVC 学习(七)JSON

9. JSON 9.1 简介 JSON&#xff08;JavaScript Object Notation&#xff0c;JS 对象标记&#xff09;是一种轻量级数据交换格式&#xff0c;采用独立于编程语言的文本格式储存和表示数据&#xff0c;易于机器解析和生成&#xff0c;提升网络传输效率。 任何 JavaScript 支持…

房产政策松绑,VR看房助力市场回春

近日房贷利率、房产限购开始松绑&#xff0c;房地产市场逐渐被激活&#xff0c;房产行业的线上服务能力&#xff0c;也愈发的受到了重视。随着房贷利率、首付比例变化的消息逐渐推出&#xff0c;部分用户开始入手房产市场&#xff0c;因此房产行业的线上服务也需要不断升级&…

关于ElementUI之动态树+数据表格+分页实例

目录 一.ElementUI动态树 二.实例 2.1.数据表 2.2.后端 2.3.前端 三.书籍管理 3.1.数据表 3.2.后端 3.2.前端 好啦今天就分享到这了&#xff0c;希望能帮到你哦&#xff01;&#xff01;&#xff01; 一.ElementUI动态树 ElementUI提供了一个动态树组件&#xff08;Dynami…

c++图像的边缘检测

图像的边缘检测 cv::Canny 是 OpenCV 中用于进行边缘检测的函数&#xff0c;特别是用于检测图像中的边缘。Canny 边缘检测是一种广泛使用的技术&#xff0c;它能够识别图像中的边缘&#xff0c;这些边缘通常表示对象之间的边界或图像中的显著特征 void cv::Canny(const cv::M…

python(自4) xpath下载 lxml安装 lxml语法 使用方式

&#xff08;一&#xff09;安装 搜索xpath 讲解 XPath 教程 (w3school.com.cn) 一&#xff0c;下载地址 &#xff1a; https://chrome.zzzmh.cn/info/hgimnogjllphhhkhlmebbmlgjoejdpjl 二 &#xff0c;拖拽 &#xff08;二&#xff09;lxml安装 cmd 打开终端 cd pythond…

踩坑 | vue动态绑定img标签src属性的一系列报错

文章目录 踩坑 | vue项目运行后使用require()图片也不显示问题描述vue中动态设置img的src不生效问题的原因require is not defined 解决办法1&#xff1a;src属性直接传入地址解决办法2 踩坑 | vue项目运行后使用require()图片也不显示 问题描述 在网上查阅之后&#xff0c;发…

org.postgresql.util.PSQLException: Bad value for type long

项目用 springbootmybatis mybatisplus&#xff0c; 数据库是&#xff1a;postgresql 。 执行查询时候返回错误。 org.springframework.dao.DataIntegrityViolationException: Error attempting to get column city_id from result set. Cause: org.postgresql.util.PSQLExce…

Matlab中clear,close all,clc功能详细说明

背景&#xff1a; 我们在写matlab程序时&#xff0c;首行总是先敲入&#xff1a;clear; close all; clc;&#xff0c;但你真的知道这三句话的具体作用嘛&#xff0c;下面进行详细说明和演示。 一、clear的功能 clear的功能&#xff1a;清理工作区变量&#xff0c;不清理前是…

Unity中Shader需要了解的点与向量

文章目录 前言一、点和向量的区别二、向量加法减法1、向量加法2、向量减法(可以把向量减法转化为向量加法) 三、向量的模四、标量![在这里插入图片描述](https://img-blog.csdnimg.cn/03df81df3cdf47989a11605d5f5e7da5.png)1、向量与标量的乘法 前言 Unity中Shader了解使用的…

真·Redis缓存优化—97%的优化率你见过嘛? | 京东云技术团队

本文通过一封618前的R2M(公司内部缓存组件&#xff0c;可以认为等同于Redis)告警&#xff0c;由浅入深的分析了该告警的直接原因与根本原因&#xff0c;并根据原因提出相应的解决方法&#xff0c;希望能够给大家在排查类似问题时提供相应的思路。 一、问题排查 1.1 邮件告警 …

将切分的图片筛选出有缺陷的

将切分的图片筛选出有缺陷的 需求代码 需求 由于之前切分的图像有一些存在没有缺陷&#xff0c;需要再次筛选 将可视化的图像更改后缀 更改为xml的 可视化代码 可视化后只有7000多个图像 原本的图像有1W多张 代码 # 按照xml文件删除对应的图片 # coding: utf-8 from P…

服务网关Gateway_微服务中的应用

没有服务网关 问题&#xff1a; 地址太多安全性管理问题 为什么要使用服务网关 网关是微服务架构中不可或缺的部分。使用网关后&#xff0c;客户端和微服务之间的网络结构如下。 注意&#xff1a; 网关统一向外部系统&#xff08;如访问者、服务&#xff09;提供REST API。在Sp…

DEV gridview多表头设计

先上图&#xff1a; 第一步转化gridview变成bandedGridview类型 一步步按照自己想要的格式添加&#xff0c;先把表头格式全部弄好&#xff0c;然后在拖拉对应的列。 注意&#xff1a;全部弄完后把列表头设置不可见

基于微信小程序的快递配送管理平台系统设计与实现(源码+lw+部署文档+讲解等)

文章目录 前言系统主要功能&#xff1a;具体实现截图论文参考详细视频演示为什么选择我自己的网站自己的小程序&#xff08;小蔡coding&#xff09;有保障的售后福利 代码参考源码获取 前言 &#x1f497;博主介绍&#xff1a;✌全网粉丝10W,CSDN特邀作者、博客专家、CSDN新星计…

建议阿里、百度、华为们,不要着急抢行业大模型的“饭碗”!

大数据产业创新服务媒体 ——聚焦数据 改变商业 近几个月&#xff0c;国内大模型领域一个很明显的发展态势&#xff0c;就是大家扎堆行业大模型。不仅各个垂直领域的企业发布多个行业大模型&#xff0c;而且百度、阿里巴巴、华为、腾讯、京东等头部巨头&#xff0c;也把行业大…

面试打底稿⑤ 项目一的第一部分

简历原文 抽查部分 项目描述 该项目旨在服务广州地区的快递物流&#xff0c;实现了下单、快递员取派件、订单转运单、线路规划、网点设置等功能。 责任描述 登录系统优化&#xff0c;双token三验证模式实现设置token状态、提高登录安全性的效果 模拟问答 1.能简单介绍一下…

当网络设置为自动获取dns时而实际nds是8.8.8.8,1.1.1.1的解决方法

笔记本换网络环境后&#xff0c;网络设置的是自动获取IP和自动获取dns。但使用命令&#xff1a;config/all命令时发现dns总是8.8.8.8,1.1.1.1。导致csdn上不了。 8.8.8.8,1.1.1.1&#xff1a;是谷歌的dns。 解决办法&#xff1a; 在支行中输入regedit打开注册表后&#xff0…

什么是Redux?它的核心概念有哪些?

聚沙成塔每天进步一点点 ⭐ 专栏简介⭐ 什么是Redux&#xff1f;⭐ 它的核心概念有哪些&#xff1f;⭐ 写在最后 ⭐ 专栏简介 前端入门之旅&#xff1a;探索Web开发的奇妙世界 欢迎来到前端入门之旅&#xff01;感兴趣的可以订阅本专栏哦&#xff01;这个专栏是为那些对Web开发…

【ACL2023】Event Extraction as Question Generation and Answering

论文题目&#xff1a;Event Extraction as Question Generation and Answering 论文来源&#xff1a;ACL2023 论文链接&#xff1a;Event Extraction as Question Generation and Answering - ACL Anthology 代码链接&#xff1a;GitHub - dataminr-ai/Event-Extraction-as-…