基于ASP.NET Core api 的服务器事件发送
現如今程序員對Web API的調用已經是輕車熟路。但是傳統的api調用都是拉模式,也就是主動發起請求去調用一個api.
但是程序員往往對另一種很有用的模式很陌生,即推模式。
拉模式 - 主動調用并獲取結果的模式。
推模式 - 訂閱并接受數據推送的模式。
今天要介紹的是一個被大家忽略但卻非常有用的一項技術。
基于HTTP/2的標準服務器事件推送模式,英文簡稱Server-Sent Events,后面簡稱SSE。
Server-Sent Events
這里引用MDN上的一段解釋:
EventSource 是服務器推送的一個網絡事件接口。一個EventSource實例會對HTTP服務開啟一個持久化的連接,以text/event-stream` 格式發送事件, 會一直保持開啟直到被要求關閉。
一旦連接開啟,來自服務端傳入的消息會以事件的形式分發至你代碼中。如果接收消息中有一個事件字段,觸發的事件與事件字段的值相同。如果沒有事件字段存在,則將觸發通用事件。
與 WebSockets,不同的是,服務端推送是單向的。數據信息被單向從服務端到客戶端分發. 當不需要以消息形式將數據從客戶端發送到服務器時,這使它們成為絕佳的選擇。例如,對于處理社交媒體狀態更新,新聞提要或將數據傳遞到客戶端存儲機制(如IndexedDB或Web存儲)之類的,EventSource無疑是一個有效方案。
以上解釋簡單說明了SSE的用途,該項技術也是推模式的典型技術之一。同類型的技術是WebSockets, 和SignalR。
代碼是展示一項技術的最好辦法:
SSE客戶端實現
//javascript 構造一個EventSource實例,代表一個服務器推送長連接。var source = new EventSource("/api/values"); //傳入支持推送模式的api url。隨后展示。//當普通消息被傳遞時觸發。source.onmessage = function (event) {console.log('onmessage: ' + event.data);};//當連接打開時觸發。source.onopen = function(event) {console.log('onopen');};//當出錯時候觸發source.onerror = function(event) {console.log('onerror');}//使用自定義時間時候觸發。ping為自定義事件,你可以根據實際需求定義自己的事件名稱。如果事件名稱匹配,則該方法會被調用source.addEventListener("ping", function(event) {console.log('onping' + event.data);});ASP.NET Core Api 實現
[HttpGet]public async Task GetValue(){//測試,debug到這里的時候你會發現,協議使用的是HTTP/2. APS.NET Core 2.1以上就默認支持HTTP/2,無需額外的配置。再Windows Server2016/Windows10+會自動提供支持。string requestProtocol = HttpContext.Request.Protocol;var response = Response;//響應頭部添加text/event-stream,這是HTTP/2協議的一部分。response.Headers.Add("Content-Type", "text/event-stream");for (int i=0;i<100;i++){// event:ping event是事件字段名,冒號后面是事件名稱,不要忘了\n換行符。await HttpContext.Response.WriteAsync($"event:ping\n");// data: 是數據字段名稱,冒號后面是數據字段內容。注意數據內容僅僅支持UTF-8,不支持二進制格式。// data后面的數據當然可以傳遞JSON String的。await HttpContext.Response.WriteAsync($"data:Controller {i} at {DateTime.Now}\r\r");// 寫入數據到響應后不要忘記 FlushAsync(),因為該api方法是異步的,所以要全程異步,調用同步方法會報錯。await HttpContext.Response.Body.FlushAsync();//模擬一個1秒的延遲。await Task.Delay(1000);}//數據發送完畢后關閉連接。Response.Body.Close();}協議的字段解釋(來源MDN)
event
事件類型.如果指定了該字段,則在客戶端接收到該條消息時,會在當前的EventSource對象上觸發一個事件,事件類型就是該字段的字段值,你可以使用addEventListener()方法在當前EventSource對象上監聽任意類型的命名事件,如果該條消息沒有event字段,則會觸發onmessage屬性上的事件處理函數.
data
消息的數據字段.如果該條消息包含多個data字段,則客戶端會用換行符把它們連接成一個字符串來作為字段值.
id
事件ID,會成為當前EventSource對象的內部屬性"最后一個事件ID"的屬性值.
retry
一個整數值,指定了重新連接的時間(單位為毫秒),如果該字段值不是整數,則會被忽略.
消息的例子:
event: userconnect data: {"username": "bobby", "time": "02:33:48"}event: usermessage data: {"username": "bobby", "time": "02:34:11", "text": "Hi everyone."}event: userdisconnect data: {"username": "bobby", "time": "02:34:23"}event: usermessage data: {"username": "sean", "time": "02:34:36", "text": "Bye, bobby."}這里提醒大家去仔細閱讀消息的格式。消息格式錯誤,將導致數據無法正確解析。
實現效果
看到類型為eventsource
看到事件類型為ping,每個一秒數據被推送到客戶端一次
長連接,請求一直未完成。
Server-Sent Events vs WebSockets
到這里大家不難發現SSE技術其實也是一種實時服務端推送數據的技術,但是他的光環被更為閃耀的WebSockets給覆蓋了。Stackoverflow上有一篇很不錯的對比。大家可以搜索WebSockets vs. Server-Sent events/EventSource
總的來說,SSE能實現的功能WebSockets都能實現,但是SEE更輕量無需其他庫并且服務器端也非常容易編寫。同時她是基于HTTP的,自然在穿透防火墻方面也就無需太多顧慮。更多的對比結論如下(參考。
SSE 相對于 Websockets 的優勢:
通過簡單的 HTTP 而不是自定義協議傳輸
可以使用第三方庫支持不支持SSE的瀏覽器例如IE瀏覽器。
內置支持重新連接和事件 ID
更簡單的協議
企業防火墻進行數據包檢查不會有問題。
Websockets 相對于 SSE 的優勢:
實時,雙向通信。
更多瀏覽器的原生支持
SSE 的理想用例:
股票行情流,數據監控大屏數據發送,IOT物聯網設備數據發送。
社交平臺上的狀態更新等。
瀏覽器通知
SSE缺陷:
不支持二進制
最大打開連接數限制。HTTP1.1 單個瀏覽器只支持6個連接(也就是打開6個Tab頁以上就會出問題,并且在Chrom和Firefox上無法解決)HTTP/2上默認100個。
總結
SSE是一項輕量級的基于HTTP/2的標準協議,除了IE瀏覽器(不包含Edge)以外的其他瀏覽器均已支持該協議。后端服務ASP.NET Core 無疑也是完美支持的,.NET程序員可以放心使用。因為SSE使用HTTP最大的優勢可以避過防火墻的坑,WebSockets協議在穿透防火墻時候可能會有問題,而且有些老式的中間設備可能不認識WebSockets協議導致兼容性問題。
SSE另一個優點是無需額外的庫就可以啟用,而且支持自動重連。非常適合從服務器推送數據到客戶端的單項應用。而不用無腦的考慮WebSockets協議。甚至可以吧SSE 和 Websokets協議聯合起來使用,當僅需服務器單項傳輸時候采用SSE,需要雙向傳輸時候采用Websokets.
總結
以上是生活随笔為你收集整理的基于ASP.NET Core api 的服务器事件发送的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 使用 Playwright 对 ASP.
- 下一篇: BeetleX服务网关流量控制