当前位置: 首页 > 编程日记 > 正文

springboot实现SSE服务端主动向客户端推送数据,java服务端向客户端推送数据,kotlin模拟客户端向服务端推送数据

SSE服务端推送

服务器向浏览器推送信息,除了 WebSocket,还有一种方法:Server-Sent Events(以下简称 SSE)。本文介绍它的用法。

在很多业务场景中,会涉及到服务端向客户端发起推送通知,但HTTP 协议无法做到服务器主动推送信息。

如何实现呢? 很多人知道WebSocket,使用长连接,实现客户端与服务端的全双工通信。但在一些场景,如支付的回调功能,这时候我们的业务只有一个功能点需要用到服务器的推送,为了一个支付功能去建立长连接,从而实现服务器推送又有些过度设计,并不合理。

但是,有一种变通方法,就是服务器向客户端声明,接下来要发送的是流信息(streaming)。

也就是说,发送的不是一次性的数据包,而是一个数据流,会连续不断地发送过来。这时,客户端不会关闭连接,会一直等着服务器发过来的新的数据流,视频播放就是这样的例子。本质上,这种通信就是以流信息的方式,完成一次用时很长的下载。

SSE 就是利用这种机制,使用流信息向浏览器推送信息。它基于 HTTP 协议,目前除了 IE/Edge,其他浏览器都支持。

接下来模拟一种网络支付场景,使用SSE,该如何实现这个过程呢?

image-20220314114431233

  1. 用户扫码向支付系统(微信、支付宝、苹果)进行支付。
  2. 支付完成之后,告知服务端我已经发起支付了(建立SSE连接)。
  3. 支付系统告诉服务端(支付宝、微信的做法),或者客户端将支付凭证传给服务器做校验(IAP),这个用户确实支付成功了。
  4. 服务端向用户发送消息:你已经支付成功,跳转到支付成功页面。(通过SSE连接,由服务器端告知用户客户端浏览器)。

这里,我们先建立SSE连接,然后在支付后将结果使用SSE推送给客户端(浏览器)。两步。

客户端需要注意的是两个概念: 事件源、事件

var es = new EventSource('事件源名称') ;  //与事件源建立连接
//标准事件处理方法,还有onopen、onerror
es.onmessage = function(e) {
};
//可以监听自定义的事件名称
es.addEventListener('自定义事件名称', function(e) {
});

通过对事件进行自定义,我们可以进行不同的操作处理,比如订单完成,订单失效等等。

一、模拟客户端

index.html

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>SSE</title>
</head>
<body>
<div id = "message">//这里展示支付状态信息
</div>
<script>if (window.EventSource) { //判断浏览器是否支持SSE//第2步,主动进行建立长连接,表明用户已经发起支付let source = new EventSource('http://127.0.0.1:8844/orderpay?payid=1');let innerHTML = '';//监听服务器端发来的事件:opensource.onopen = function(e) {innerHTML += "onopen:准备就绪,可以开始接收服务器数据" + "<br/>"; //支付结果document.getElementById("message").innerHTML = innerHTML;};//监听服务器端发来的事件:messagesource.onmessage = function(e) {innerHTML += "onmessage:" + e.data + "<br/>"; //支付结果document.getElementById("message").innerHTML = innerHTML;};//自定义finish事件,主动关闭EventSourcesource.addEventListener('finish', function(e) {source.close();innerHTML += "支付结果接收完毕,通知服务端关闭EventSource" +  "<br/>";document.getElementById("message").innerHTML = innerHTML;}, false);//监听服务器端发来的事件:errorsource.onerror = function(e) {if (e.readyState === EventSource.CLOSED) {innerHTML += "sse连接已关闭" +  "<br/>";} else {console.log(e);}};} else {console.log("你的浏览器不支持SSE");}
</script></body>
</html>

二、模拟服务端

  • java版本:
@RestController
@CrossOrigin
@RequestMapping
public class SSEControler {//建立之后根据订单id,将SseEmitter存到ConcurrentHashMap//正常应该存到数据库里面,生成数据库订单,这里我们只是模拟一下public static final ConcurrentHashMap<Long, SseEmitter> sseEmitters= new ConcurrentHashMap<>();//第2步:接受用户建立长连接,表示该用户已支付,已支付就可以生成订单(未确认状态)@GetMapping("/orderpay")public SseEmitter orderpay(Long payid) {//设置默认的超时时间60秒,超时之后服务端主动关闭连接。SseEmitter emitter = new SseEmitter(60 * 1000L);sseEmitters.put(payid,emitter);emitter.onTimeout(() -> sseEmitters.remove(payid));return emitter;}//第3步:接受支付系统的支付结果告知,表明用户支付成功@GetMapping("/payback")public void payback (Long payid){//把SSE连接取出来SseEmitter emitter = sseEmitters.get(payid);try {//第4步:由服务端告知浏览器端:该用户支付成功了emitter.send("用户支付成功"); //触发前端message事件。//触发前端自定义的finish事件emitter.send(SseEmitter.event().name("finish").id("6666").data("哈哈"));} catch (IOException e) {emitter.completeWithError(e);   //出发前端onerror事件}}
}
  • kotlin版本
@RestController
@CrossOrigin
@RequestMapping
class Hello {//第2步:接受用户建立长连接,表示该用户已支付,已支付就可以生成订单(未确认状态)@GetMapping("/orderpay")fun orderpay( @RequestParam("payid")payid: Long): SseEmitter {println("接受连接建立")//设置默认的超时时间60秒,超时之后服务端主动关闭连接。val emitter = SseEmitter(60 * 1000L)sseEmitters.put(payid, emitter)emitter.onTimeout { sseEmitters.remove(payid) }return emitter}//第3步:接受支付系统的支付结果告知,表明用户支付成功@GetMapping("/payback")fun payback(@RequestParam("payid")payid: Long) {//把SSE连接取出来val emitter = sseEmitters[payid]try {println("支付成功")//第4步:由服务端告知浏览器端:该用户支付成功了emitter!!.send("用户支付成功") //触发前端message事件。
//            emitter.send(SseEmitter.event().name("nothing").id("1").data("哈哈"))//触发前端自定义的finish事件emitter.send(SseEmitter.event().name("finish").id("1").data("哈哈"))} catch (e: IOException) {emitter!!.completeWithError(e) //出发前端onerror事件}}companion object {//建立之后根据订单id,将SseEmitter存到ConcurrentHashMap//正常应该存到数据库里面,生成数据库订单,这里我们只是模拟一下val sseEmitters = ConcurrentHashMap<Long, SseEmitter>()}
}

三、测试

在浏览器打开页面,页面会自动与服务器建立连接,这里自动完成了第一步,发起支付。

第二步:postman或者浏览器调用

localhost:8844/payback?payid=1

模拟支付成功,服务器回调客户端。

结果如下:

image-20220314142053416

demo见:https://gitee.com/ck_567/springboot-sse.git

相关文章:

总结一下在使用某里云服务器的过程中出现过的一些问题

此文总结在使用阿里云的过程中出现过的问题 想起来就记录一下 本人为言行负责&#xff01; 海内选择腾讯华为&#xff0c;共勉&#xff01; k8s不同命名空间的配置文件串掉了 k8s集群服务器购买上后&#xff0c;无法使用外网。连续购买了4台然后退款&#xff0c;每一次都是…

上位机图像处理和嵌入式模块部署(qt插件的使用)

【 声明:版权所有,欢迎转载,请勿用于商业用途。 联系信箱:feixiaoxing @163.com】 一个软件一般有很多的功能,但是主流程只有一个。但在软件开发的过程当中,一般来说功能是需要不断添加的,但是主流程最好不要轻易修改。这里的插件就相当于各种各样的功能,而主流程就是如何怎么去调用这些插件的功能。所以,今天正好来学一下怎么添加qt插件,个人觉得这部分还是非常重要的。

mysql的varchar长度到底能插多少字符?

需要注意:这点和oracle的varchar2不同,oracle是字节数长度,不是字符长度,一个汉字占2个字节,所以长度6最多只能存3个汉字。varchar(6)表示可以插入6个汉字,或6个字母数字,或汉字和字母数字共6个,是表示插入的字符数,不是字节数。看下Oracle的测试:汉字占用2个字节。

运用ETLCloud快速实现数据清洗、转换

自动化数据转换和集成工具,可实现企业内部和外部数据的无缝对接。

配置nginx+keepalived高可用代理数据库ip端口

需求:配置nginx+keepalived高可用反向代理数据库ip端口(数据库服务器无法增加新SCAN IP或者需要隐藏数据库IP的情况下适用)本机ip为:192.168.20.10和192.168.20.11。2.任意节点关机或重启系统,浮动ip也会自动漂移到另外节点。1.任意节点停nginx:浮动ip会自动漂移到另外节点。安装依赖包和nginx和keepalived。浮动IP为:192.168.20.20。配置keepalived.conf。两台centos7.9。

k8s图形化管理工具之rancher

在前面的k8s基础学习中,我们学习了各种资源的搭配运用,以及命令行,声明式文件创建。这些都是为了k8s管理员体会k8s的框架,内容基础。在真正的生产环境中,大部分的公司还是会选用图形化管理工具来管理k8s集群,大大提高工作效率。在二进制搭建k8集群时,我们就知道了k8s本身就具有一款原生的k8s集群管理工具,但是原生图形化管理工具dashborad只拥有管理一个集群的能力。而对于现代化生产力公司来讲,一个集群能够做的事情还是太少,所以我们需要引入更强大的集群管理工具。

TCP服务器最多支持多少客户端连接

本文从理论和实际两个方面介绍了一个 TCP 服务器支持的最大连接数

C程序的内存空间布局(栈、堆、数据区、常量区、代码区)

较详细的介绍了栈、堆、数据区、常量区、代码区

给服务器开通telnet的流程

但一些特殊场景下,比如要升级ssh,ssh不能用时,需要使用telnet,用过要关闭此服务。需要首先安装,如果telnet-server服务在xinetd之前安装了,要先删除telnet-server,再安装xinetd。安装顺序:xinetd--》telnet--》telnet-server。安装顺序:xinetd--》telnet--》telnet-server。2、卸载rpm包(如果已经安装了,又不清楚顺序,可以都卸载后统一安装)注意:telnet-server服务启动依赖xinetd服务,

NRF24L01模块传输MPU6050数据,接收端数据一直为0问题记录

问题描述:一、发射端1、正确配置NRF模块,以及测试过能够正常通信,在发射端的发射线程中进行了如下操作2、这里是获取了陀螺仪的x轴数据,将其而分为两个8位的数据存入发送缓冲区中。因为一个陀螺仪x轴数据是16位的,所以对其进行了拆分,这里只获取gyro的x轴数据进行发送,目的是进行测试。3、这个是发送函数,只要把发送缓冲区的地址作为参数传入就可以发送了。二、接收端1、接收端的NRF24L01模块也正确配置后,在接收线程中进行如下操作2、读取NRF传输过来的数据,存到接收BUF中,然后打印

Java中的四种访问权限(private,public,protected,无修饰)

/实体类属性和数据库字段名称不一致//实体类属性和数据库字段名称不一致return id;return age;emp.test();//直接调用public修饰的变量//private修饰的变量进行赋值//调用private修饰的变量1、public修饰符定义的属性和方法通过对象实例化进行调用,2、private修饰的属性通过set、get方法进行调用。

YOLOv5中Ghostbottleneck结构shortcut=True和shortcut=False有什么区别

GhostBotleneck结构中的shodcut=True和shorcut=False的区别在干是否使用残差连接。当shorcu=True时,使用残差连接,可以以加速模型的收敛速度和提高模型的准确率,当shorcu=False时,不使用残差连接,可以减少模型的参数数量和计算量。实际上不只是Ghostbottleneck具有残差连接,在C3、C2f等具有Bottleneck模块的结构均可根据此例举一反三。残差块是深度卷积神经网络中的一种基本模块,可以有效地解决梯度消失和梯度爆炸的问题。

如何在Nginx中配置防盗链?

防盗链是一种防止网站资源被非法下载的技术。当用户尝试直接访问一个受保护的资源时,服务器会返回一个403 Forbidden错误,提示用户该资源受到保护,不能直接访问。这样可以避免用户通过搜索引擎或其他方式获取到未经授权的资源。通过以上步骤,我们可以在Linux系统中的Nginx Web服务器中使用Shell脚本实现防盗链的配置。这种方法可以有效地保护网站资源不被非法下载,提高用户体验,同时防止恶意攻击。在实际项目中,我们可以根据实际需求灵活配置受保护资源的URL和处理方式。

说说你对 TypeScript 的理解?与 JavaScript 的区别?

超集,不得不说另外一个概念,子集,怎么理解这两个呢,举个例子,如果一个集合 A 里面的的所有元素集合 B 里面都存在,那么我们可以理解集合 B 是集合 A 的超集,集合 A 为集合 B 的子集。其是一种静态类型检查的语言,提供了类型注解,在代码编译阶段就可以检查出数据类型的错误。通过类型批注提供在编译时启动类型检查的静态类型,这是可选的,而且可以忽略而使用。如果缺乏声明而不能推断出类型,那么它的类型被视作默认的动态。等数据格式,对象的类型就是用接口来描述的。的语法,所以任何现有的。对于基本类型的批注是。

vue3项目中使用vite-plugin-mock

-设置模拟数据的存储文件夹,如果不是index.js需要写明完整路径。supportTs?: boolean;--是否读取ts文件模块,设置为true时不能读取js文件。--是否在控制台显示请求日志。4、利用axios调用。

什么是数据中台?

说完了数据中台诞生的历史背景,现在,我们应该对数据中台有了一定的了解,那我们现在给数据中台下个定义。自2016年,数据中台被提出以来,不同的人对数据中台有不同的理解,就像一千个读者心中有一千个哈姆雷特,因此也有许多不同的定义,以下是我从一些文章、书籍中搜集到的关于数据中台的定义:数据中台是DT时代的大背景下,为实现数据快(快速)、准(准确)、省(低成本)赋能业务发展的目标,将企业的数据统一整合起来,基于Onedata方法论借助大数据平台完成数据的统一加工处理,对外提供数据服务的一套机制。

万字详解数据仓库、数据湖、数据中台和湖仓一体

数字化转型浪潮卷起各种新老概念满天飞,数据湖、数据仓库、数据中台轮番在朋友圈刷屏,有人说“数据中台算个啥,数据湖才是趋势”,有人说“再见了数据湖、数据仓库,数据中台已成气候”……企业还没推开数字化大门,先被各种概念绊了一脚。那么它们 3 者究竟有啥区别?别急,先跟大家分享两个有趣的比喻。1、图书馆VS地摊如果把数据仓库比喻成“图书馆”,那么数据湖就是“地摊”。去图书馆借书(数据),书籍质量有保障,但你得等,等什么?等管理员先查到这本书属于哪个类目、在哪个架子上,你才能精准拿到自己想要的书;

详解static关键字在Java中的各种适应场景,这个很重要!

写在开头 静态(static)关键字在Java的众多关键字中举足轻重!也是很多大厂面试官特别喜欢问的面试高频知识点,今天我们就一起学习,梳理,重温一下这个关键吧,之所以用重温修饰是因为在过往的文章中早已提到了static,无论是变量中还是方法中甚至于类中,static都是不可或缺的! 我们知道要想调

SpringBoot3集成Zookeeper

ZooKeeper是一个集中的服务,用于维护配置信息、命名、提供分布式同步、提供组服务。分布式应用程序以某种形式使用所有这些类型的服务。

websocket服务端本地部署

即登录cpolar官网后,点击预留,保留一个固定tcp端口地址,然后将其配置到相应的隧道中即可。这里我们用cpolar内网穿透来映射内网端口,它支持http/https/tcp协议,不限制流量,无需公网ip,也不用设置路由器,操作简单。注意:该隧道选择的是临时tcp地址和端口,24小时内会变化,如需固定tcp地址,可升级为专业套餐做tcp地址固定!cpolar安装成功后,默认会配置两个默认隧道:一个ssh隧道和一个website隧道,可自行删减或者修改。,可以查看到token码,复制并执行命令进行认证。

使用DockerFile构建镜像与镜像上传

首先Dockerfile 是一个文本格式的配置文件, 用户可以使用 Dockerfile 来快速创建自定义的镜像。

微信小程序之WXSS模板样式、页面配置(.json)和网络数据请求

一、WXSS 模板样式1、什么是 WXSS2、WXSS 和 CSS 的关系二、WXSS 模板样式 - rpx1、什么是 rpx 尺寸单位2、rpx 的实现原理3、rpx 与 px 之间的单位换算*三 、WXSS 模板样式 - 样式导入1、什么是样式导入2、@import 的语法格式四、WXSS 模板样式 - 全局样式和局部样式1、全局样式2、局部样式五、页面配置1、页面配置文件的作用2、页面配置和全局配置的关系3、页面配置中常用的配置项。

Redis 击穿、穿透、雪崩产生原因解决思路

也就是在设定的时间里数据没有取出来,但是锁由过期了,常见的思路是,锁过期时间值递增,但是想想不靠谱,因为第一个请求可能超时,如果后面的也超时呢,接连多次超时之后,锁过期时间值势必特别大了,这样做弊端太多。雪崩,和击穿类似,不同的是击穿是一个热点Key某时刻失效,而雪崩是大量的热点Key在一瞬间失效,网络上很多博客都在强调解决雪崩的策略是随机过期时间,这个非常不准确,举个例子,银行做活动,之前这个利息系数为2%,过了零点系数改为3%,这种情况能将用户的对应的key改为随机过期吗?如果用的过去的数据叫脏数据。

spring和springboot、springMVC有什么区别?

今天来聊一下,刚在面试中被问到的一个经典问题Spring 提供了广泛的功能用于企业级应用开发Spring Boot 简化了 Spring 应用的开发和部署Spring MVC 则是专注于构建 Web 应用的 MVC 框架在使用时,你可以根据项目需求选择合适的组件或组合使用它们。在很多现代的 Spring 应用中,特别是微服务架构中,Spring Boot 和 Spring MVC 经常一起使用。好了,以上就是本文的全部内容,如有问题欢迎留言讨论。

Java中的方法重载和方法重写有什么区别?

Java中的方法重载(Overloading)和方法重写(Overriding)都是面向对象编程中的重要概念,但它们之间有一些区别。方法重载是指在同一个类中,可以定义多个具有相同名称但参数列表不同的方法。这些方法具有不同的参数类型、参数个数或参数顺序。在调用重载方法时,Java编译器会根据传递给方法的参数类型和数量来选择要调用的正确方法。方法重载主要用于解决方法的命名冲突和提高代码的可读性和可维护性。

python基础使用之变量,表达式,语句

PYTHON基础知识系列之变量、表达式、语句

C语言常见面试题:什么是宏,宏的作用是什么?

宏在计算机科学中是一种批量处理程序命令,它是一种抽象的规则或模式,用于说明某一特定输入(通常是字符串)如何根据预定义的规则转换成对应的输出(通常也是字符串)。在编译时,预处理器会对宏进行展开,即将宏的内容替换到宏所在的位置。以上是宏的一些主要作用,但并不是全部。在实际编程中,根据需要选择是否使用宏以及如何使用宏,以实现更好的代码组织和可读性。,这样就可以计算出a和b的和。这个例子展示了宏的基本用法和作用。在这个例子中,我们定义了一个宏。,用于计算两个数的和。时,预处理器会将其展开为。

python基础小知识:引用和赋值的区别

通过引用,就可以在程序范围内任何地方传递大型对象而不必在途中进行开销巨大的赋值操作。不过需要注意的是,这种赋值仅能做到顶层赋值,如果出现嵌套的情况下仍不能进行深层赋值。赋值与引用不同,复制后会产生一个新的对象,原对象修改后不会影响到新的对象。如果在原位置修改这个可变对象时,可能会影响程序其他位置对这个对象的引用

MySQL数据库查询语句之组函数,子查询语句

当一个SQL的执行需要借助另一个SQL的执行结果时,则需要进行SQL嵌套,该语法结构称之为子查询。先筛选出符合要求的数据,再对符合要求的数据进行分组时,分组的工作量会被减少,效率更高。先确定从哪张表进行操作-->对表中数据进行分组-->基于分组结果进行查询操作。执行顺序:优先执行小括号内的子SQL,根据子SQL的执行结果再执行外层SQL。执行顺序:from-->where-->group by-->select。执行顺序:from-->group by-->select。

k8s 使用tomcat官方镜像部署集群并解决访问页面404

官方镜像这里有个坑,使用kubectl启动之后,页面报错404,仔细检查发现,是因为tomcat的webapp目录下没有对应的文件,所以连初始界面都无法显示。要想显示,必须要根据官方镜像自己构建一个Dockerfile。根据上面的信息可以看出,该POD部署在k8s-node1上,映射POD的8080端口到master的30088端口上。这里需要将镜像上传到自己搭建的registry,并配置nodes节点都可以正常访问5000端口。三、根据官方镜像自己构建一个一次性就能启动的Tomcat镜像。