SSE服务端推送

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

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

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

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

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

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

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

  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

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

结果如下:

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

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

  1. java spring 服务器关闭连接_java springboot websocket 服务 服务器主动关闭连接 导致 抛出java.io.EOFException异常...

    遇到这个问题不要慌,去查查众说纷纭.那我就在这里总结一下吧 存在此问题的有以下几种情况 1.ws连接不稳定经常断线: 答: 1)可能是客户和服务器之间的网络问题 2)可能是服务端内存不够用导致线程被异 ...

  2. http协议与https协议+UDP协议和TCP协议+WebSocket协议下服务端主动去发送信息+对称加密与非对称加密+get和post请求方式区别详解+浏览器内核以及jsj解析引擎

    TCP和UDP协议是TCP/IP协议的核心. 在TCP/IP网络体系结构中,TCP(传输控制协议,Transport Control Protocol).UDP(用户数据报协议,User Data P ...

  3. netty tcp服务端主动断开客户端_「Netty核心技术」6-ChannelPipeline源码

    ChannelPipeline是Channelhandler的容器,它负责ChannelHandler的管理和事件拦截与调度. 土话: ChannelPipeline就是用来管理Channelhand ...

  4. flux服务器推消息,服务端主动推送数据,除了 WebSocket 你还能想到啥?

    原标题:服务端主动推送数据,除了 WebSocket 你还能想到啥? 来自公众号: 江南一点雨 在 上篇文章 中,松哥和大家分享了 WebFlux 的基本用法,小伙伴们已经了解到使用 WebFlux ...

  5. 服务端主动推送数据,除了 WebSocket 你还能想到啥?

    在上篇文章中,松哥和大家分享了 WebFlux 的基本用法,小伙伴们已经了解到使用 WebFlux 我们的返回值可以是 Mono 也可以是 Flux,如果是 Flux,由于 Flux 中包含多个元素, ...

  6. 使用SignalR从服务端主动推送警报日志到各种终端(桌面、移动、网页)

    工作上有个业务,.Net Core WebAPI作为服务端,需要将运行过程中产生的日志分类,并实时推送到各种终端进行报警,终端有桌面(WPF).移动(Xamarin.Forms).网站(Angular ...

  7. Netty-Server-Hander自定义解码器-服务端主动推送

    netty server端使用自定义解码器,通过存储client连接实现主动推送消息,并发送自定义心跳包 Server端 依赖 <dependency><groupId>io. ...

  8. springboot整合sse

    链接: SpringBoot 实现SSE 服务器发送事件 链接: SpringBoot 实现SSE 服务器发送事件 链接: Springboot之整合SSE实现消息推送 链接: springboot ...

  9. SpringBoot使用SSE推送消息

    序言   服务器推送技术在一些需要实时数据的开发场景会被广泛使用,要获得实时数据,前端轮询的方式明显是被摒弃的,大部分人第一时间想到的就是websocket.之前的一个项目也是用的websocket, ...

最新文章

  1. 数据结构与算法-学习笔记(13)
  2. sql server 2005 优化方法
  3. 本网站的幻灯片浏览很好看,不懂谁有这代码?
  4. mysql5.5 mysqli_php5.5.38增加mysqli扩展
  5. python psutil 获取命令历史_Python使用psutil获取进程信息的例子
  6. python程序写诗_将Python诗歌与D结合起来
  7. Linux下php-fpm进程过多导致内存耗尽问题解决
  8. 【robot framework】GET/POST请求
  9. 解决谷歌自带翻译不出现问题
  10. 五.java入门【循环语句】
  11. 离线地图数据tpk的制作
  12. 网络安全-古典加密算法之置换密码加解密算法
  13. C语言miller rabin算法,浅谈miller_rabin算法和pollard_rho算法
  14. The Balance POJ - 2142
  15. GPRS使用AT指令发短信拨号
  16. 类的封装与对象数组-随机点名器
  17. 南京广义软件:Android lunch分析以及产品分支构建
  18. :[转贴]变态级JAVA程序员面试32问(附答案
  19. 评估软件项目的经济价值
  20. 我模拟了一个机器人后,对分层思想的更多感受

热门文章

  1. 2022-2028年中国小球产业深度调研及投资前景预测报告(全卷)
  2. Go 学习笔记(59)— Go 第三方库之 etcd/clientv3 封装为方法使用(建立连接、设置key-value、获取key-value、获取带前缀的key-value)
  3. 【牛腩新闻发布系统】开始后端02
  4. scheduled sampling_seq2seq
  5. 存在量词后必须用合取式?-数学
  6. [翻译]Python中yield的解释
  7. SpringBoot整合MyBatis详细教程~
  8. 会议重点介绍芯片设计中的人工智能
  9. CUDA 11功能展示
  10. 2021年大数据Hadoop(四):Hadoop发行版公司