栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

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

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

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

SSE服务端推送

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

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

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

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

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

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

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

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

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

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

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

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

一、模拟客户端

index.html




    
    SSE



    //这里展示支付状态信息
二、模拟服务端

java版本:

@RestController
@CrossOrigin
@RequestMapping
public class SSEControler {
    //建立之后根据订单id,将SseEmitter存到ConcurrentHashMap
    //正常应该存到数据库里面,生成数据库订单,这里我们只是模拟一下
    public static final ConcurrentHashMap 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()
    }
}
三、测试

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

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

localhost:8844/payback?payid=1

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

结果如下:

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

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/762752.html

Java相关栏目本月热门文章

我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号