探索SSE:實時Web通信的學(xué)習之旅

Hello大家好,今天一起來學(xué)習SSE(Server-Sent Events)。使用go語言實現(xiàn)服務(wù)器推送。
幾種web實時通信機制
輪詢
Websocket
Server-Sent Events
輪詢
顧名思義,輪詢就是在某個時間間隔內(nèi)定期向服務(wù)器發(fā)送請求。其中輪詢有分為短輪詢和長輪詢。這是一種客戶端主動請求的方式。
短輪詢
定期向服務(wù)器請求,無論請求的資源是否可用,服務(wù)器都會盡快響應(yīng),客戶端再次發(fā)起下次輪詢。這種方式會比較消耗網(wǎng)絡(luò)帶寬,如果資源一直不可用就會有很多不必要的請求發(fā)送到服務(wù)器。

長輪詢
定期向服務(wù)器發(fā)送請求,與短輪詢不同的是,在資源不可用時長輪詢不會立即將連接關(guān)閉,而是會等待資源可用后在響應(yīng)客戶端?;蛘叩却艘欢螘r間資源任然不可用(超時)服務(wù)器將連接關(guān)閉,客戶端等待一段時間后再次發(fā)起請求。

與短輪詢相比,長輪詢更高效一些,請求數(shù)量減少了很多。
Websocket
在客戶端和服務(wù)器打開交互式的通信會話。這是一種全雙工通信,客戶端與服務(wù)器會建立一個持久連接,服務(wù)器可以主動發(fā)送數(shù)據(jù)給客戶端。客戶端可以通過監(jiān)聽事件來處理來自服務(wù)器的消息。與輪詢的方式相比,大大減少了延遲,沒有了數(shù)據(jù)更新的往返時間。

Server-Sent Events
服務(wù)器發(fā)送事件,SSE會建立一個持久的HTTP連接。建立連接后服務(wù)器可以主動往客戶端推送數(shù)據(jù)。與websocket不同,這是一種單向通信的方式,即建立連接后客戶端不能向服務(wù)器發(fā)送數(shù)據(jù)。

通常來說,一個網(wǎng)頁獲取新的數(shù)據(jù)通常需要發(fā)送一個請求到服務(wù)器,也就是向服務(wù)器請求的頁面。使用服務(wù)器發(fā)送事件,服務(wù)器可以隨時向我們的 Web 頁面推送數(shù)據(jù)和信息。這些被推送進來的信息可以在這個頁面上以 事件 + 數(shù)據(jù) 的形式來處理。
使用場景
實時數(shù)據(jù)更新:SSE適用于需要在客戶端實時更新數(shù)據(jù)的場景。例如,股票市場行情、實時新聞更新、社交媒體的實時通知等。
實時監(jiān)控和通知:SSE可用于監(jiān)控系統(tǒng)、設(shè)備或傳感器的實時狀態(tài)。服務(wù)器可以將實時數(shù)據(jù)推送給客戶端,以便實時監(jiān)測并及時采取相應(yīng)行動。例如,實時監(jiān)控溫度、濕度、能源消耗等。
實時協(xié)作和協(xié)同編輯:SSE可以在協(xié)作和協(xié)同編輯工具中提供實時更新功能。多個用戶可以同時編輯同一個文檔,并通過SSE實時更新對方的更改,以實現(xiàn)實時協(xié)作和協(xié)同編輯的效果。
消息推送和通知:SSE可用于發(fā)送實時消息和通知給訂閱的用戶。例如,推送新郵件通知、提醒用戶活動提醒、推送定制的實時提醒等。
事件流是一個簡單的文本數(shù)據(jù)流,文本應(yīng)該用UTF8格式編碼。事件流的消息由兩個換行符分開,以冒號開頭的行為注釋行,會被忽略。

事件流字段
event:?用于標識事件類型的字符串,如果沒有指定event,瀏覽器默認認為是message。
data:?消息的數(shù)據(jù)字段,當EventSource收到多個已```data:```開頭的連續(xù)行是,會將它們連接起來,在它們之間插入一個換行符。,末尾的換行符也會被刪除。
id:?事件ID,會被設(shè)置為當前EventSource對象的內(nèi)部屬性“最后一個事件ID”的值。
retry:?重新連接的時間。如果與服務(wù)器的連接丟失,瀏覽器會等待指定的時間,然后重新連接。retry必須是一個整數(shù),它的單位是毫秒。
使用
SSE的用法比較簡單,只需要在服務(wù)器編寫一些代碼將事件流傳輸?shù)角岸?。前端使用EventSource來監(jiān)聽這些事件流。
使用JavaScript創(chuàng)建一個EventSource對象,連接到localhost:8083/events。監(jiān)聽消息流的message事件(如果沒有指定event,那么event默認是message)

服務(wù)器發(fā)送事件流
服務(wù)器端發(fā)送事件流也比較簡單,這里使用Go語言做了一個demo。
首先我們創(chuàng)建一個名字為Event的struct,表示SSE中的一個事件。這個struct有兩個方法
String() string,將Event編碼為字符串
Bytes() []byte,將Event編碼為字節(jié)切片

創(chuàng)建一個HTTP 控制器,負責輸出事件流。下面這個stream函數(shù)會輸出100個事件到客戶端,每隔1秒鐘輸出一個事件。這里注意,一個事件寫入到連接后不要忘記把緩存數(shù)據(jù)flush掉,這樣客戶端才能看到最新推送的數(shù)據(jù)。

代碼放在https://github.com/raojinlin/server-sent-events-demo/tree/main,有興趣的通信可以看看。
注意事項
nginx配置
使用nginx做反向代理時需要將proxy_buffering關(guān)閉
proxy_buffering off
或者加上響應(yīng)頭部x-accel-buffering,這樣nginx就不會給后端響應(yīng)數(shù)據(jù)加buffer
x-accel-buffering: no
EventSource
連接關(guān)閉后會自動重連
需要顯示的調(diào)用close方法
EventSource.prototype.close
參考
https://developer.mozilla.org/zh-CN/docs/Web/API/Server-sent_events/Using_server-sent_events