后端主動向前端推送消息 Server-Sent Events


后端向前端推送消息的常見方式有 websocket、輪詢等方式。還有一種方式:Server-Sent Event (簡稱SSE)。

SSE本質

 嚴格說,HTTP 協議無法做到服務器主動推送消息。但是,有一種變通方法,就是服務器向客戶端聲明,接下來要發送的是流消息(streaming)。

也就是說,發送的不是一次性的數據包,而是一個數據流,會連續不斷地發送過來。這時,客戶端不會關閉連接,會一直等着服務器發送新的數據流,視頻播放就是這樣的例子。本質上,這種通信就是以流信息的方式,完成一次用時很長的下載。

SSE 就是利用這種機制,使用流消息向瀏覽器推送信息,它基於 HTTP 協議。

 SSE特點

SSE 與 WebSocket 作用相似,都是建立瀏覽器與服務器之間的通信渠道,然后服務器向瀏覽器推送消息。

SSE 是單向通道,只能服務器向瀏覽器發送,因為流信息本質上就是下載。

SSE的優點如下:

  1. SSE 使用 HTTP 協議;WebSocket 是一個獨立協議。
  2. SSE 屬於輕量級,使用簡單;WebSocket 協議相對復雜。
  3. SSE 默認支持斷線重試;WebSocket 需要自己實現。
  4. SSE 一般只用來傳送文本,二進制數據需要編碼后傳送;WebSocket 默認支持傳送二進制數據。
  5. SSE 支持自定義發送的消息類型。

客戶端

 SSE客戶端 API 部署在 EventSource 對象上。

    // SSE 的客戶端 API 部署在 EventSource 對象上
    if ('EventSource' in window) {
        // 瀏覽器支持 SSE

    }

    // 使用 SSE 時,瀏覽器首先生成一個 EventSource 實例,向服務器發起連接
    var url = "http:127.0.0.1:8080/subscribe";
    /* 上面的 url 可以與當前網址同域,也可以跨域。
     * 跨域時,可以指定第二個參數,打開 withCredentials 屬性,表示是否一起發送cookie
     * 示例 var source = new EventSource(url, { withCredentials: true });
     */
    var source = new EventSource(url);

    // 連接一旦建立,就會觸發 open 事件,可以在 onopen 屬性定義回調函數
    source.onopen = function (event) {
        // ...
    };

    // 客戶端收到服務器發來的數據,就會觸發 message 事件,可以在 onmessage 屬性定義回調函數
    source.onmessage = function (event) {
        // data 就是服務器傳回的數據(文本格式)
        var data = event.data;
        // handle message
    };

    // 如果發生通信錯誤(比如連接中斷),就會觸發 error 事件,可以在 onerror 屬性定義回調函數
    source.onerror = function (event) {
        // handle error event
    };

    // 其中 close 方法用於關閉 SSE 連接
    // source.close();

上述代碼示例了 EventSource 建立監理,監聽消息,異常等情況的處理方式。

 默認情況下,服務器發來數據時,總是觸發 EventSource 實例 message 事件。開發者還可以自定義 SSE 事件,這種情況下,發送回來的數據不會觸發 message 事件。

    source.addEventListener('foo', function (event) {
        var data = event.data;
        // handle message
    }, false);

上面是自定義事件的實例代碼。

服務端

服務端用 Java 編寫,采用 SpringBoot 框架,其中 SpringBoot 采用內置的 SseEmitter 對象支持 SSE。

    @GetMapping("/subscribe")
    public SseEmitter subscribe() {
        // 設置超時時間為5分鍾
        SseEmitter sseEmitter = new SseEmitter(5*60*1000L);
        // 直接返回 SseEmitter 對象就可以和客戶端連接
        return sseEmitter;
    }

 客戶端請求上面的接口之后就可以建立連接。如果要往客戶端發送消息,只需要調用 SseEmitter 實例的 send 方法。

        // 調用 SseEmitter 的 send 方法即可往客戶端發送消息
        try {
            sseEmitter.send("hello");
        } catch (IOException e) {
            e.printStackTrace();
        }

實際運用時,可以將用戶 id 和 SseEmitter 實例存在 Map 中,當需要給某個用戶推送消息時,先通過用戶 id 查找到其對應的 SseEmitter 實例,然后調用 send 方法,即可以給對應的用戶推送消息。 

 如果服務端要斷開連接,調用 SseEmitter 的 complete 方法即可。

小結

客戶端 js 的代碼中核心類是 EventSource,其包括建立連接、接收消息、異常處理時觸發的事件。客戶端 SpringBoot 框架中核心類是 SseEmitter 其中記錄連接的信息,調用其 send 方法就可以向客戶端推送消息。

 

參考:

本文內容大多參考以下文章,非原創。

http://www.ruanyifeng.com/blog/2017/05/server-sent_events.html

https://www.cnblogs.com/yihuihui/p/12622729.html

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM