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
<!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模拟客户端向服务端推送数据的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 继承WebMvcConfigurer 和
- 下一篇: 总结一下在使用某里云服务器的过程中出现过