摘要:不過永久幀的技術會導致主頁面的加載條始終處于狀態,體驗很差。同時,規范允許服務端指定自定義事件,客戶端偵聽該事件即可。
服務端推
服務端推,指的是由服務器主動的向客戶端發送消息(響應)。在應用層的HTTP協議實現中,“請求-響應”是一個round trip,它的起點來自客戶端,因此在應用層之上無法實現簡易的服務端推功能。當前解決服務端推送的方案有這幾個:
客戶端長輪訓
websocket雙向連接
iframe永久幀
長輪訓雖然可以避免短輪訓造成的服務端過載,但在服務端返回數據后仍需要客戶端主動發起下一個長輪訓請求,等待服務端響應,這樣仍需要底層的連接建立而且服務端處理邏輯需要相應處理,不符合邏輯上的流程簡單的服務端推送;
websocket連接相對而言功能最強大,但是它對服務器的版本有要求,在可以使用websocket協議的服務器上盡量采用此種方式;
iframe永久幀則是在在頁面嵌入一個專用來接受數據的iframe頁面,該頁面由服務器輸出相關信息,如,服務器不停的向iframe中寫入類似的script標簽和數據,實現另一種形式的服務端推送。不過永久幀的技術會導致主頁面的加載條始終處于“loading”狀態,體驗很差。
HTML5規范中提供了服務端事件EventSource,瀏覽器在實現了該規范的前提下創建一個EventSource連接后,便可收到服務端的發送的消息,這些消息需要遵循一定的格式,對于前端開發人員而言,只需在瀏覽器中偵聽對應的事件皆可。
相比較上文中提到的3中實現方式,EventSource流的實現方式對客戶端開發人員而言非常簡單,兼容性上出了IE系的瀏覽器(IE、Edge)外其他都良好;對于服務端,它可以兼容老的瀏覽器,無需upgrade為其他協議,在簡單的服務端推送的場景下可以滿足需求。在瀏覽器與服務端需要強交互的場景下,websocket仍是不二的選擇。
EventSource規范簡析 瀏覽器端瀏覽器端,需要創建一個EventSource對象,并且傳入一個服務端的接口URI作為參數。
var evtSource = new EventSource("http://localhost:9111/es");
其中,"http://localhost:9111/es"為服務端吐出數據的接口。目前,EventSource在大多數瀏覽器端不支持
跨域,因此它不是一種跨域的解決方案。
默認EventSource對象通過偵聽“message”事件獲取服務端傳來的消息,“open”事件則在http連接建立后觸發,”error“事件會在通信錯誤(連接中斷、服務端返回數據失敗)的情況下觸發。同時,EventSource規范允許服務端指定自定義事件,客戶端偵聽該事件即可。
evtSource.addEventListener("message",function(e){ console.log(e.data); }); evtSource.addEventListener("error",function(e){ console.log(e); })服務端
事件流的對應MIME格式為text/event-stream,而且其基于HTTP長連接。針對HTTP1.1規范默認采用長連接,針對HTTP1.0的服務器需要特殊設置。
服務端返回數據需要特殊的格式,它分為四種消息類型:
event, data, id, retry
其中,event指定自定義消息的名稱,如event: customMessagen;
data指定具體的消息體,可以是對象或者字符串,如data: JSON.stringify(jsonObj) ,在消息體后面有兩個換行符n,代表當前消息體發送完畢,一個換行符標識當前消息并未結束,瀏覽器需要等待后面數據的到來后再觸發事件;
id為當前消息的標識符,可以不設置。一旦設置則在瀏覽器端的eventSource對象中就會有體現(假設服務端返回id: 369n),eventSource.lastEventId == 369。該字段使用場景不大;
retry設置當前http連接失敗后,重新連接的間隔。EventSource規范規定,客戶端在http連接失敗后默認進行重新連接,重連間隔為3s,通過設置retry字段可指定重連間隔;
每個字段都有名稱,緊接著有個”:“。當出現一個沒有名稱的字段而只有”:“時,這就會被服務端理解為”注釋“,并不會被發送至瀏覽器端,如: commision。
由于EventSource是基于HTTP連接之上的,因此在一段沒有數據的時期會出現超時問題。服務器默認HTTP超時時間為2分鐘,在node端可以通過response.connection.setTimeou(0)設置為默認的2min超時, 因此需要服務端做心跳保活,否則客戶端在連接超時的情況下出現net::ERR_INCOMPLETE_CHUNKED_ENCODING錯誤。通過閱讀相關規范,發現注釋行可以用來防止連接超時,服務器可以定期發送一條消息注釋行,以保持連接不斷。
下面提供koa的服務端代碼:
var fs = require("fs"); var path = require("path"); var PassThrough = require("stream").PassThrough; var Readable = require("stream").Readable; var koa = require("koa"); var Router = require("koa-router"); var app = new koa(); var router = new Router(); function RR(){ Readable.call(this,arguments); } RR.prototype = new Readable(); RR.prototype._read = function(data){ } router.get("/",function(ctx,next){ ctx.set("content-type","text/html"); ctx.body = fs.readFileSync(path.join(process.cwd(),"eventServer.html")); }); const sse = (stream,event, data) => { return stream.push(`event:${ event } data: ${ JSON.stringify(data) } `) // return stream.write(`event:${ event } data: ${ JSON.stringify(data) } `); } router.get("/es",function(ctx,next){ var stream = new RR()//PassThrough(); ctx.set({ "Content-Type":"text/event-stream", "Cache-Control":"no-cache", Connection: "keep-alive" }); sse(stream,"test",{a: "yango",b: "tango"}); ctx.body = stream; setInterval(()=>{ sse(stream,"test",{a: "yango",b: Date.now()}); },3000); }); app.use(router.routes()); app.listen(9111,function(){ console.log("listening port 9111"); });
此處需要注意的是koa-router的返回值必須是一個Stream(Readable),這是由于koa的特殊性造成的。如果context.body不是Stream是一個字符串或者Buffer實例,會直接在node原生中調用res.end(buffer),結束了HTTP響應:
koa lib/application.js // responses if (Buffer.isBuffer(body)) return res.end(body); if ("string" == typeof body) return res.end(body); if (body instanceof Stream) return body.pipe(res);
因此造成了服務端事件流無法正確響應。而返回Stream類型的方式有幾種,如通過擴展stream模塊的Readable可讀流返回或者直接采用PassThrough流返回,亦可通過through2模塊或者Transform對象實現,歸根到底保證可以從該stream對象中pipe出數據至http.ServerResponse對象中。
附頁面代碼
參考資料hello world
使用服務器發送事件
EventSource超時
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/89422.html
eventSource簡單介紹 eventSource是用來解決web上服務器端向客戶端推送消息的問題的。不同于ajax輪詢的復雜和websocket的資源占用過大,eventSource(sse)是一個輕量級的,易使用的消息推送api 如何使用 客戶端代碼 Document (function() { var sour...
閱讀 2026·2019-08-30 15:52
閱讀 2987·2019-08-29 16:09
閱讀 1333·2019-08-28 18:30
閱讀 2461·2019-08-26 12:24
閱讀 1107·2019-08-26 12:12
閱讀 2281·2019-08-26 10:45
閱讀 578·2019-08-23 17:52
閱讀 839·2019-08-23 16:03