摘要:不過(guò)永久幀的技術(shù)會(huì)導(dǎo)致主頁(yè)面的加載條始終處于狀態(tài),體驗(yàn)很差。同時(shí),規(guī)范允許服務(wù)端指定自定義事件,客戶(hù)端偵聽(tīng)該事件即可。
服務(wù)端推
服務(wù)端推,指的是由服務(wù)器主動(dòng)的向客戶(hù)端發(fā)送消息(響應(yīng))。在應(yīng)用層的HTTP協(xié)議實(shí)現(xiàn)中,“請(qǐng)求-響應(yīng)”是一個(gè)round trip,它的起點(diǎn)來(lái)自客戶(hù)端,因此在應(yīng)用層之上無(wú)法實(shí)現(xiàn)簡(jiǎn)易的服務(wù)端推功能。當(dāng)前解決服務(wù)端推送的方案有這幾個(gè):
客戶(hù)端長(zhǎng)輪訓(xùn)
websocket雙向連接
iframe永久幀
長(zhǎng)輪訓(xùn)雖然可以避免短輪訓(xùn)造成的服務(wù)端過(guò)載,但在服務(wù)端返回?cái)?shù)據(jù)后仍需要客戶(hù)端主動(dòng)發(fā)起下一個(gè)長(zhǎng)輪訓(xùn)請(qǐng)求,等待服務(wù)端響應(yīng),這樣仍需要底層的連接建立而且服務(wù)端處理邏輯需要相應(yīng)處理,不符合邏輯上的流程簡(jiǎn)單的服務(wù)端推送;
websocket連接相對(duì)而言功能最強(qiáng)大,但是它對(duì)服務(wù)器的版本有要求,在可以使用websocket協(xié)議的服務(wù)器上盡量采用此種方式;
iframe永久幀則是在在頁(yè)面嵌入一個(gè)專(zhuān)用來(lái)接受數(shù)據(jù)的iframe頁(yè)面,該頁(yè)面由服務(wù)器輸出相關(guān)信息,如,服務(wù)器不停的向iframe中寫(xiě)入類(lèi)似的script標(biāo)簽和數(shù)據(jù),實(shí)現(xiàn)另一種形式的服務(wù)端推送。不過(guò)永久幀的技術(shù)會(huì)導(dǎo)致主頁(yè)面的加載條始終處于“l(fā)oading”狀態(tài),體驗(yàn)很差。
HTML5規(guī)范中提供了服務(wù)端事件EventSource,瀏覽器在實(shí)現(xiàn)了該規(guī)范的前提下創(chuàng)建一個(gè)EventSource連接后,便可收到服務(wù)端的發(fā)送的消息,這些消息需要遵循一定的格式,對(duì)于前端開(kāi)發(fā)人員而言,只需在瀏覽器中偵聽(tīng)對(duì)應(yīng)的事件皆可。
相比較上文中提到的3中實(shí)現(xiàn)方式,EventSource流的實(shí)現(xiàn)方式對(duì)客戶(hù)端開(kāi)發(fā)人員而言非常簡(jiǎn)單,兼容性上出了IE系的瀏覽器(IE、Edge)外其他都良好;對(duì)于服務(wù)端,它可以兼容老的瀏覽器,無(wú)需upgrade為其他協(xié)議,在簡(jiǎn)單的服務(wù)端推送的場(chǎng)景下可以滿(mǎn)足需求。在瀏覽器與服務(wù)端需要強(qiáng)交互的場(chǎng)景下,websocket仍是不二的選擇。
EventSource規(guī)范簡(jiǎn)析 瀏覽器端瀏覽器端,需要?jiǎng)?chuàng)建一個(gè)EventSource對(duì)象,并且傳入一個(gè)服務(wù)端的接口URI作為參數(shù)。
var evtSource = new EventSource("http://localhost:9111/es");
其中,"http://localhost:9111/es"為服務(wù)端吐出數(shù)據(jù)的接口。目前,EventSource在大多數(shù)瀏覽器端不支持
跨域,因此它不是一種跨域的解決方案。
默認(rèn)EventSource對(duì)象通過(guò)偵聽(tīng)“message”事件獲取服務(wù)端傳來(lái)的消息,“open”事件則在http連接建立后觸發(fā),”error“事件會(huì)在通信錯(cuò)誤(連接中斷、服務(wù)端返回?cái)?shù)據(jù)失敗)的情況下觸發(fā)。同時(shí),EventSource規(guī)范允許服務(wù)端指定自定義事件,客戶(hù)端偵聽(tīng)該事件即可。
evtSource.addEventListener("message",function(e){ console.log(e.data); }); evtSource.addEventListener("error",function(e){ console.log(e); })服務(wù)端
事件流的對(duì)應(yīng)MIME格式為text/event-stream,而且其基于HTTP長(zhǎng)連接。針對(duì)HTTP1.1規(guī)范默認(rèn)采用長(zhǎng)連接,針對(duì)HTTP1.0的服務(wù)器需要特殊設(shè)置。
服務(wù)端返回?cái)?shù)據(jù)需要特殊的格式,它分為四種消息類(lèi)型:
event, data, id, retry
其中,event指定自定義消息的名稱(chēng),如event: customMessagen;
data指定具體的消息體,可以是對(duì)象或者字符串,如data: JSON.stringify(jsonObj) ,在消息體后面有兩個(gè)換行符n,代表當(dāng)前消息體發(fā)送完畢,一個(gè)換行符標(biāo)識(shí)當(dāng)前消息并未結(jié)束,瀏覽器需要等待后面數(shù)據(jù)的到來(lái)后再觸發(fā)事件;
id為當(dāng)前消息的標(biāo)識(shí)符,可以不設(shè)置。一旦設(shè)置則在瀏覽器端的eventSource對(duì)象中就會(huì)有體現(xiàn)(假設(shè)服務(wù)端返回id: 369n),eventSource.lastEventId == 369。該字段使用場(chǎng)景不大;
retry設(shè)置當(dāng)前http連接失敗后,重新連接的間隔。EventSource規(guī)范規(guī)定,客戶(hù)端在http連接失敗后默認(rèn)進(jìn)行重新連接,重連間隔為3s,通過(guò)設(shè)置retry字段可指定重連間隔;
每個(gè)字段都有名稱(chēng),緊接著有個(gè)”:“。當(dāng)出現(xiàn)一個(gè)沒(méi)有名稱(chēng)的字段而只有”:“時(shí),這就會(huì)被服務(wù)端理解為”注釋“,并不會(huì)被發(fā)送至瀏覽器端,如: commision。
由于EventSource是基于HTTP連接之上的,因此在一段沒(méi)有數(shù)據(jù)的時(shí)期會(huì)出現(xiàn)超時(shí)問(wèn)題。服務(wù)器默認(rèn)HTTP超時(shí)時(shí)間為2分鐘,在node端可以通過(guò)response.connection.setTimeou(0)設(shè)置為默認(rèn)的2min超時(shí), 因此需要服務(wù)端做心跳保活,否則客戶(hù)端在連接超時(shí)的情況下出現(xiàn)net::ERR_INCOMPLETE_CHUNKED_ENCODING錯(cuò)誤。通過(guò)閱讀相關(guān)規(guī)范,發(fā)現(xiàn)注釋行可以用來(lái)防止連接超時(shí),服務(wù)器可以定期發(fā)送一條消息注釋行,以保持連接不斷。
下面提供koa的服務(wù)端代碼:
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的返回值必須是一個(gè)Stream(Readable),這是由于koa的特殊性造成的。如果context.body不是Stream是一個(gè)字符串或者Buffer實(shí)例,會(huì)直接在node原生中調(diào)用res.end(buffer),結(jié)束了HTTP響應(yīng):
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);
因此造成了服務(wù)端事件流無(wú)法正確響應(yīng)。而返回Stream類(lèi)型的方式有幾種,如通過(guò)擴(kuò)展stream模塊的Readable可讀流返回或者直接采用PassThrough流返回,亦可通過(guò)through2模塊或者Transform對(duì)象實(shí)現(xiàn),歸根到底保證可以從該stream對(duì)象中pipe出數(shù)據(jù)至http.ServerResponse對(duì)象中。
附頁(yè)面代碼
參考資料hello world
使用服務(wù)器發(fā)送事件
EventSource超時(shí)
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/89422.html
eventSource簡(jiǎn)單介紹 eventSource是用來(lái)解決web上服務(wù)器端向客戶(hù)端推送消息的問(wèn)題的。不同于ajax輪詢(xún)的復(fù)雜和websocket的資源占用過(guò)大,eventSource(sse)是一個(gè)輕量級(jí)的,易使用的消息推送api 如何使用 客戶(hù)端代碼 Document (function() { var sour...
摘要:本文則試著和讀者一起對(duì)這個(gè)數(shù)據(jù)推送的需求進(jìn)行技術(shù)方案的探究。數(shù)據(jù)推送有兩種替代方案無(wú)更新方案和數(shù)據(jù)拉取方案。數(shù)據(jù)拉取和數(shù)據(jù)推送的功能目標(biāo)是一致的讓用戶(hù)看到最新的數(shù)據(jù)。但數(shù)據(jù)推送有一些優(yōu)勢(shì),即更低的延遲。 前言 眾所周知,AJAX的出現(xiàn)是前端快速發(fā)展的一個(gè)標(biāo)志,同時(shí)也是前后端得以分離的重要基礎(chǔ)。作為一個(gè)C/S網(wǎng)絡(luò)的web系統(tǒng),網(wǎng)絡(luò)通信在發(fā)揮著舉足輕重的作用。大部分的場(chǎng)景下,我們是主動(dòng)觸發(fā)...
摘要:本文則試著和讀者一起對(duì)這個(gè)數(shù)據(jù)推送的需求進(jìn)行技術(shù)方案的探究。數(shù)據(jù)推送有兩種替代方案無(wú)更新方案和數(shù)據(jù)拉取方案。數(shù)據(jù)拉取和數(shù)據(jù)推送的功能目標(biāo)是一致的讓用戶(hù)看到最新的數(shù)據(jù)。但數(shù)據(jù)推送有一些優(yōu)勢(shì),即更低的延遲。 前言 眾所周知,AJAX的出現(xiàn)是前端快速發(fā)展的一個(gè)標(biāo)志,同時(shí)也是前后端得以分離的重要基礎(chǔ)。作為一個(gè)C/S網(wǎng)絡(luò)的web系統(tǒng),網(wǎng)絡(luò)通信在發(fā)揮著舉足輕重的作用。大部分的場(chǎng)景下,我們是主動(dòng)觸發(fā)...
閱讀 2021·2019-08-30 15:52
閱讀 2975·2019-08-29 16:09
閱讀 1323·2019-08-28 18:30
閱讀 2452·2019-08-26 12:24
閱讀 1089·2019-08-26 12:12
閱讀 2272·2019-08-26 10:45
閱讀 565·2019-08-23 17:52
閱讀 810·2019-08-23 16:03