1. 前言
今天專案經理交給我一個開發任務。如果有人下了訂單就給後臺一個通知,也就是伺服器推送功能。這個需求不是很複雜就一個通知功能,如果我用websocket來做還要搞個websocket伺服器,而且還有不少配置。websocket是全雙工通訊,單向通訊簡直是殺雞用牛刀。用輪詢吧,浪費伺服器資源不說,還不一定實時,訂單處理慢了豈不是怠慢了客戶。有沒有別的選擇呢?當然有!
2. SSE推送技術
SSE全稱Server-sent Events,是HTML 5 規範的一個組成部分,具體去MDN網站檢視相關文件。該規範十分簡單,主要由兩個部分組成:第一個部分是伺服器端與瀏覽器端之間的通訊協議,第二部分則是在瀏覽器端可供 JavaScript 使用的 EventSource 物件。通訊協議是基於純文字的簡單協議。伺服器端的響應的內容型別是“text/event-stream”。響應文字的內容可以看成是一個事件流,由不同的事件所組成。每個事件由型別和資料兩部分組成,同時每個事件可以有一個可選的識別符號。不同事件的內容之間通過僅包含回車符和換行符的空行(“rn”)來分隔。每個事件的資料可能由多行組成。
如上圖所示,每個事件之間通過空行來分隔。每一行都是由鍵值對組成。如果鍵為空則表示該行為註釋,會在處理時被忽略。例如第10行。
第1行表示一個只包含資料的事件。會按照預設事件走(message事件)。第3-4代表一個附帶eventID的事件。第6-8代表一個自定義事件。第10-14代表一個多行資料事件,多行資料由換行符連結
key定義有以下幾種:
- data,表示該行包含的是資料。以 data 開頭的行可以出現多次。所有這些行都是該事件的資料。
- 型別為 event,表示該行用來宣告事件的型別。瀏覽器在收到資料時,會產生對應型別的事件。預設提供三個標準事件(當然你可以自定義):
- id,表示該行用來宣告事件的識別符號。伺服器端返回的資料中包含了事件的識別符號,瀏覽器會記錄最近一次接收到的事件的識別符號。如果與伺服器端的連線中斷,當瀏覽器端再次進行連線時,會通過 HTTP 頭“Last-Event-ID”來宣告最後一次接收到的事件的識別符號。伺服器端可以通過瀏覽器端傳送的事件識別符號來確定從哪個事件開始來繼續連線。
- retry,表示該行用來宣告瀏覽器在連線斷開之後進行再次連線之前的等待時間。
SSE只適用於高階瀏覽器,但是注意IE不直接支援。IE上的XMLHttpRequest物件不支援獲取部分的響應內容,所以不支援。每次總有IE怪不得快被淘汰了。
3. SSE VS Websocket
- SSE 只能Server到Client單項,而Websocket是雙向通訊。
- SSE 比 Websocket 輕量。當然功能要簡單的多。開發便利,不牽涉協議升級問題。
- SSE 天然支援斷線重連
4. Spring Mvc中的SSE
Spring Mvc對SSE進行了支援。如果你要宣告一個SSE連線。只需要在你的控制器宣告一個如下介面:
必須必須返回SseEmitter物件,SseEmitter物件是Session級別的,如果你要點對點針對每個session要獨立儲存。如果你是廣播可以公用一個SseEmitter物件。按照SSE規範也必須宣告produces為"text/event-stream"。當你呼叫該介面的時候將建立起SSE連線。
你可以在另一個執行緒中呼叫SseEmitter的send方法向客戶端傳送事件。你也可以在傳送事件後呼叫complete方法來關閉SSE連線。
5. 客戶端
由於SSE 是HTML5規範。所以對於APP端必須有HTML才能支援。並且IE如果要支援需要使用一些相容開發包,比如polyfill庫。客戶端因為只接受事件所以開發比較簡單:
- 宣告客戶端連線, 初始化EventSource物件。
- 編寫監聽器來監聽事件。
6. 總結
今天介紹了SSE 服務端推送。和長輪訓、comet、websocket相比比較輕量級。在一些需要伺服器實時推送規模不大的業務場景實現更簡單點。相信看了本文後你會很快入門。在實際開發中要根據業務對這幾種推送進行技術選型。沒有最好的只有最適合的。只不過SSE對大多數開發者來說不夠熟悉。相關的demo我已經上傳到碼雲倉庫:
https://gitee.com/felord/sse-...關注公眾號:碼農小胖哥 獲取更多資訊