后端主動向前端推送消息 Server-Sent Events
后端向前端推送消息的常見方式有 websocket、輪詢等方式。還有一種方式:Server-Sent Event (簡稱SSE)梯刚。
SSE本質(zhì)
?嚴格說,HTTP 協(xié)議無法做到服務(wù)器主動推送消息左腔。但是,有一種變通方法吼驶,就是服務(wù)器向客戶端聲明董虱,接下來要發(fā)送的是流消息(streaming)。
也就是說获讳,發(fā)送的不是一次性的數(shù)據(jù)包阴颖,而是一個數(shù)據(jù)流,會連續(xù)不斷地發(fā)送過來赔嚎。這時膘盖,客戶端不會關(guān)閉連接胧弛,會一直等著服務(wù)器發(fā)送新的數(shù)據(jù)流尤误,視頻播放就是這樣的例子。本質(zhì)上结缚,這種通信就是以流信息的方式损晤,完成一次用時很長的下載。
SSE?就是利用這種機制红竭,使用流消息向瀏覽器推送信息尤勋,它基于?HTTP?協(xié)議。
?SSE特點
SSE?與?WebSocket?作用相似茵宪,都是建立瀏覽器與服務(wù)器之間的通信渠道最冰,然后服務(wù)器向瀏覽器推送消息。
SSE?是單向通道稀火,只能服務(wù)器向瀏覽器發(fā)送暖哨,因為流信息本質(zhì)上就是下載。
SSE的優(yōu)點如下:
SSE?使用?HTTP?協(xié)議凰狞;WebSocket?是一個獨立協(xié)議篇裁。
SSE?屬于輕量級,使用簡單赡若;WebSocket?協(xié)議相對復(fù)雜达布。
SSE?默認支持斷線重試疤坝;WebSocket?需要自己實現(xiàn)弟蚀。
SSE?一般只用來傳送文本不同,二進制數(shù)據(jù)需要編碼后傳送;WebSocket?默認支持傳送二進制數(shù)據(jù)印蔬。
SSE?支持自定義發(fā)送的消息類型。
客戶端
?SSE客戶端?API?部署在?EventSource?對象上派哲。
// SSE 的客戶端 API 部署在 EventSource 對象上if('EventSource'in window) {
? ? ? ? // 瀏覽器支持 SSE? ? }
? ? // 使用 SSE 時偎血,瀏覽器首先生成一個 EventSource 實例,向服務(wù)器發(fā)起連接varurl = "http://127.0.0.1:8080/subscribe";
? ? /* 上面的 url 可以與當(dāng)前網(wǎng)址同域雕沉,也可以跨域集乔。
? ? * 跨域時,可以指定第二個參數(shù)坡椒,打開 withCredentials 屬性扰路,表示是否一起發(fā)送cookie
? ? * 示例 var source = new EventSource(url, { withCredentials: true });
? ? */varsource =new EventSource(url);
? ? // 連接一旦建立,就會觸發(fā) open 事件倔叼,可以在 onopen 屬性定義回調(diào)函數(shù)source.onopen =function (event) {
? ? ? ? // ...? ? };
? ? // 客戶端收到服務(wù)器發(fā)來的數(shù)據(jù)汗唱,就會觸發(fā) message 事件,可以在 onmessage 屬性定義回調(diào)函數(shù)source.onmessage =function (event) {
? ? ? ? // data 就是服務(wù)器傳回的數(shù)據(jù)(文本格式)vardata = event.data;
? ? ? ? // handle message? ? };
? ? // 如果發(fā)生通信錯誤(比如連接中斷)丈攒,就會觸發(fā) error 事件哩罪,可以在 onerror 屬性定義回調(diào)函數(shù)source.onerror =function (event) {
? ? ? ? // handle error event? ? };
? ? // 其中 close 方法用于關(guān)閉 SSE 連接// source.close();
上述代碼示例了?EventSource?建立監(jiān)理,監(jiān)聽消息巡验,異常等情況的處理方式际插。
?默認情況下,服務(wù)器發(fā)來數(shù)據(jù)時显设,總是觸發(fā)?EventSource?實例 message?事件框弛。開發(fā)者還可以自定義 SSE?事件,這種情況下捕捂,發(fā)送回來的數(shù)據(jù)不會觸發(fā) message?事件瑟枫。
source.addEventListener('foo',function (event) {
? ? ? ? vardata = event.data;
? ? ? ? // handle message},false);
上面是自定義事件的實例代碼。
服務(wù)端
服務(wù)端用 Java?編寫指攒,采用 SpringBoot?框架慷妙,其中 SpringBoot?采用內(nèi)置的 SseEmitter?對象支持?SSE。
@GetMapping("/subscribe")
? ? public SseEmitter subscribe() {
? ? ? ? // 設(shè)置超時時間為5分鐘SseEmitter sseEmitter =newSseEmitter(5*60*1000L);
? ? ? ? // 直接返回 SseEmitter 對象就可以和客戶端連接return sseEmitter;
? ? }
?客戶端請求上面的接口之后就可以建立連接允悦。如果要往客戶端發(fā)送消息膝擂,只需要調(diào)用?SseEmitter?實例的 send?方法。
// 調(diào)用 SseEmitter 的 send 方法即可往客戶端發(fā)送消息try {
? ? ? ? ? ? sseEmitter.send("hello");
? ? ? ? } catch (IOException e) {
? ? ? ? ? ? e.printStackTrace();
? ? ? ? }
實際運用時澡屡,可以將用戶 id?和?SseEmitter?實例存在 Map?中猿挚,當(dāng)需要給某個用戶推送消息時,先通過用戶?id?查找到其對應(yīng)的?SseEmitter?實例驶鹉,然后調(diào)用 send?方法绩蜻,即可以給對應(yīng)的用戶推送消息。?
?如果服務(wù)端要斷開連接室埋,調(diào)用?SseEmitter?的?complete?方法即可办绝。
小結(jié)
客戶端 js?的代碼中核心類是?EventSource伊约,其包括建立連接、接收消息孕蝉、異常處理時觸發(fā)的事件屡律。客戶端?SpringBoot?框架中核心類是?SseEmitter?其中記錄連接的信息降淮,調(diào)用其 send?方法就可以向客戶端推送消息超埋。
參考:
本文內(nèi)容大多參考以下文章,非原創(chuàng)佳鳖。
http://www.ruanyifeng.com/blog/2017/05/server-sent_events.html