|
基于 WEB 的實(shí)時(shí)事件通知方式大致有五種方案:HTTP拉取方式(pull),HTTP流,Long Polling,F(xiàn)lashXMLSocket方式,Java Applet。
首先說下Comet這個(gè)詞,Comet這個(gè)詞是最早由Alex Russell(DojoToolkit 的項(xiàng)目 Lead)提出的,稱基于 HTTP 長連接、無須在瀏覽器端安裝插件的“服務(wù)器推(Push)”技術(shù)為“Comet”。
1. HTTP拉取方式(pull)
在這種傳統(tǒng)的方法中,客戶端以用戶可定義的時(shí)間間隔去檢查服務(wù)器上的最新數(shù)據(jù)。這種拉取方式的頻率要足夠高才能保證很高的數(shù)據(jù)精確度,但高頻率可能會(huì)導(dǎo)致多余的檢查,從而導(dǎo)致較高的網(wǎng)絡(luò)流量。而另一方面,低頻率則會(huì)導(dǎo)致錯(cuò)過更新的數(shù)據(jù)。理想地,拉取的時(shí)間間隔應(yīng)該等于服務(wù)器狀態(tài)改變的速度。常見的實(shí)現(xiàn)如利用 "<meta http-equiv="refresh" c />" tag,當(dāng)然利用xmlHttpRequest定時(shí)取也是一種方法。
2. HTTP流(Push機(jī)制)
HTTP流有兩種形式:
* Page Stream: 頁面上不間斷的HTTP連接響應(yīng)(HTTP 1.1 Keep Alive).
通過在 HTML 頁面里嵌入一個(gè)隱蔵幀(iframe),然后將這個(gè)隱蔵幀的SRC屬性設(shè)為對(duì)一個(gè)長連接的請求,服務(wù)器端就能源源不斷地往客戶端輸入數(shù)據(jù)。
* Service Stream: XMLHttpRequest連接中的服務(wù)器數(shù)據(jù)流。
客戶端是在 XMLHttpRequest 的 readystate 為4(即數(shù)據(jù)傳輸結(jié)束)時(shí)調(diào)用回調(diào)函數(shù),進(jìn)行信息處理。當(dāng) readystate 為 4 時(shí),數(shù)據(jù)傳輸結(jié)束,連接已經(jīng)關(guān)閉。Mozilla Firefox 提供了對(duì) Streaming AJAX 的支持,即 readystate 為3時(shí)(數(shù)據(jù)仍在傳輸中),客戶端可以讀取數(shù)據(jù),從而無須關(guān)閉連接,就能讀取處理服務(wù)器端返回的信息。IE 在 readystate 為3時(shí),不能讀取服務(wù)器返回的數(shù)據(jù),目前 IE 不支持基于 Streaming AJAX。
注:使用 Page Stream(iframe) 請求一個(gè)長連接有一個(gè)很明顯的不足之處:IE、Morzilla Firefox下端的進(jìn)度欄都會(huì)顯示加載沒有完成,而且 IE 上方的圖標(biāo)會(huì)不停的轉(zhuǎn)動(dòng),表示加載正在進(jìn)行。Google 的天才們使用一個(gè)稱為“htmlfile”的 ActiveX 解決了在 IE 中的加載顯示問題,并將這種方法用到了 gmail+gtalk 產(chǎn)品中。Alex Russell 在 “What else is burried down in the depth's of Google's amazing JavaScript?”文章中介紹了這種方法。Zeitoun 網(wǎng)站提供的comet-iframe.tar.gz,封裝了一個(gè)基于 iframe 和 htmlfile 的 JavaScript comet 對(duì)象,支持IE、Mozilla Firefox 瀏覽器,可以作為參考。(http://alex.dojotoolkit.org/?p=538)
3. 長時(shí)間輪詢(Long Polling)
也就是所謂的異步輪詢(Asynchronous Polling),這種方式是純服務(wù)器端推送方式和客戶端拉取方式的混合。它是基于BAYEUX協(xié)議(http://svn.xantus.org/shortbus/trunk/bayeux/bayeux.html) 的。這個(gè)協(xié)議遵循基于主題的發(fā)布——訂閱機(jī)制。在訂閱了某個(gè)頻道后,客戶端和服務(wù)器間的連接會(huì)保持打開狀態(tài),并保持一段事先定義好的時(shí)間(默認(rèn)為45 秒)。如果服務(wù)器端沒有事件發(fā)生,而發(fā)生了超時(shí),服務(wù)器端就會(huì)請求客戶端進(jìn)行異步重新連接。如果有事件發(fā)生,服務(wù)器端會(huì)發(fā)送數(shù)據(jù)到客戶端,然后客戶端重新連接。
a) 服務(wù)器端會(huì)阻塞請求直到有數(shù)據(jù)傳遞或超時(shí)才返回。
b) 客戶端 JavaScript 響應(yīng)處理函數(shù)會(huì)在處理完服務(wù)器返回的信息后,再次發(fā)出請求,重新建立連接。
c) 當(dāng)客戶端處理接收的數(shù)據(jù)、重新建立連接時(shí),服務(wù)器端可能有新的數(shù)據(jù)到達(dá);這些信息會(huì)被服務(wù)器端保存直到客戶端重新建立連接,客戶端會(huì)一次把當(dāng)前服務(wù)器端所有的信息取回。
4. Flash XMLSocket(push機(jī)制)
這種方案實(shí)現(xiàn)的基礎(chǔ)是:
a) 安裝了 Flash 播放器,F(xiàn)lash 提供了 XMLSocket 類(Flash 7.0.14以上版本)。
b) JavaScript 和 Flash 的緊密結(jié)合:在 JavaScript 可以直接調(diào)用 Flash 程序提供的接口。
具體實(shí)現(xiàn)方法:在 HTML 頁面中內(nèi)嵌入一個(gè)使用了 XMLSocket 類的 Flash 程序。JavaScript 通過調(diào)用此 Flash 程序提供的套接口與服務(wù)器端的套接口進(jìn)行通信。JavaScript 在收到服務(wù)器端以 XML 格式傳送的信息后可以很容易地控制 HTML頁面的內(nèi)容顯示。
關(guān)于如何去構(gòu)建 JavaScript 與 Flash XMLSocket 的 Flash 程序,以及如何在 JavaScript 里調(diào)用 Flash 提供的接口,我們可以參考 AFLAX(Asynchronous Flash and XML)項(xiàng)目提供的 Socket Demo 以及SocketJS(請參見 [http://www.aflax.org/ Asynchronous Flash and XML,提供了強(qiáng)大的Flash、Javascript 庫和很多范例。])。
Javascript 與 Flash 的緊密結(jié)合,極大增強(qiáng)了客戶端的處理能力。從 Flash 播放器 V7.0.19 開始,已經(jīng)取消了XMLSocket 的端口必須大于 1023 的限制。Linux 平臺(tái)也支持 Flash XMLSocket 方案。但此方案的缺點(diǎn)在于:
a) 客戶端必須安裝 Flash 播放器;
b) 因?yàn)?XMLSocket 沒有 HTTP 隧道功能,XMLSocket類不能自動(dòng)穿過防火墻;
c) 因?yàn)槭鞘褂肧ocket接口,需要設(shè)置一個(gè)通信端口,防火墻、代理服務(wù)器也可能對(duì)非 HTTP 通道端口進(jìn)行限制;
d) 必須使用XML格式作為消息格式,數(shù)據(jù)冗余增大。
此方案在一些網(wǎng)絡(luò)聊天室,網(wǎng)絡(luò)互動(dòng)游戲中得到廣泛使用。
5. Java Applet(Push機(jī)制)
類似于Flash XMLSocket方式。目前已經(jīng)很少使用,原因極可能是因在手機(jī)等移動(dòng)終端缺少支持。
總結(jié)和建議:
如果我們想要高數(shù)據(jù)一致性和高網(wǎng)絡(luò)性能,我們就應(yīng)該選擇推送方式。但是,推送會(huì)帶來一些擴(kuò)展性問題;服務(wù)器應(yīng)用程序CPU使用率是拉取方式的7倍。根據(jù)TUD(http://swerl.tudelft.nl/twiki/pub/Main/TechnicalReports/TUD-SERG-2007-016.pdf)的測試結(jié)果,服務(wù)器性能會(huì)在350-500個(gè)用戶時(shí)趨于飽和。對(duì)于更大數(shù)量的用戶,服務(wù)器端需要維護(hù)大量并發(fā)的長連接。在這種應(yīng)用背景下,服務(wù)器端需要考慮負(fù)載均衡和集群技術(shù);或是在服務(wù)器端為長連接作一些改進(jìn)。
使用拉取方式,要想達(dá)到完整的數(shù)據(jù)一致性以及很高的網(wǎng)絡(luò)性能是很困難的。如果拉取的時(shí)間間隔大于數(shù)據(jù)更新的時(shí)間間隔,就會(huì)發(fā)生一些數(shù)據(jù)的遺失。而如果小于數(shù)據(jù)更新的時(shí)間間隔,網(wǎng)絡(luò)性能就會(huì)受到影響。拉取方式只有在拉取時(shí)間間隔等同于數(shù)據(jù)更新時(shí)間間隔時(shí),才會(huì)恰到好處。但是,為了達(dá)到那樣的目標(biāo),我們就需要提前知道準(zhǔn)確的數(shù)據(jù)更新時(shí)間間隔。然而,數(shù)據(jù)更新的時(shí)間間隔很少是靜態(tài)不變并可以預(yù)知的。這使得拉取方式只有在數(shù)據(jù)是根據(jù)某種特定模式發(fā)布的情況才有用。
控制信息與數(shù)據(jù)信息使用不同的 HTTP 連接
使用長連接時(shí),存在一個(gè)很常見的場景:客戶端網(wǎng)頁需要關(guān)閉,而服務(wù)器端還處在讀取數(shù)據(jù)的堵塞狀態(tài),客戶端需要及時(shí)通知服務(wù)器端關(guān)閉數(shù)據(jù)連接。服務(wù)器在收到關(guān)閉請求后首先要從讀取數(shù)據(jù)的阻塞狀態(tài)喚醒,然后釋放為這個(gè)客戶端分配的資源,再關(guān)閉連接。所以在設(shè)計(jì)上,我們需要使客戶端的控制請求和數(shù)據(jù)請求使用不同的 HTTP 連接,才能使控制請求不會(huì)被阻塞。
在實(shí)現(xiàn)上,如果是基于 iframe 流方式的長連接,客戶端頁面需要使用兩個(gè)iframe,一個(gè)是控制幀,用于往服務(wù)器端發(fā)送控制請求,控制請求能很快收到響應(yīng),不會(huì)被堵塞;一個(gè)是顯示幀,用于往服務(wù)器端發(fā)送長連接請求。如果是基于 AJAX 的長輪詢方式,客戶端可以異步地發(fā)出一個(gè) XMLHttpRequest 請求,通知服務(wù)器端關(guān)閉數(shù)據(jù)連接。
在客戶和服務(wù)器之間保持“心跳”信息
在瀏覽器與服務(wù)器之間維持一個(gè)長連接會(huì)為通信帶來一些不確定性:因?yàn)閿?shù)據(jù)傳輸是隨機(jī)的,客戶端不知道何時(shí)服務(wù)器才有數(shù)據(jù)傳送。服務(wù)器端需要確保當(dāng)客戶端不再工作時(shí),釋放為這個(gè)客戶端分配的資源,防止內(nèi)存泄漏。因此需要一種機(jī)制使雙方知道大家都在正常運(yùn)行。在實(shí)現(xiàn)上:
a) 服務(wù)器端在阻塞讀時(shí)會(huì)設(shè)置一個(gè)時(shí)限,超時(shí)后阻塞讀調(diào)用會(huì)返回,同時(shí)發(fā)給客戶端沒有新數(shù)據(jù)到達(dá)的心跳信息。此時(shí)如果客戶端已經(jīng)關(guān)閉,服務(wù)器往通道寫數(shù)據(jù)會(huì)出現(xiàn)異常,服務(wù)器端就會(huì)及時(shí)釋放為這個(gè)客戶端分配的資源。
b) 如果客戶端使用的是基于 AJAX 的長輪詢方式;服務(wù)器端返回?cái)?shù)據(jù)、關(guān)閉連接后,經(jīng)過某個(gè)時(shí)限沒有收到客戶端的再次請求,會(huì)認(rèn)為客戶端不能正常工作,會(huì)釋放為這個(gè)客戶端分配、維護(hù)的資源。
c) 當(dāng)服務(wù)器處理信息出現(xiàn)異常情況,需要發(fā)送錯(cuò)誤信息通知客戶端,同時(shí)釋放資源、關(guān)閉連接。
it知識(shí)庫:基于WEB的實(shí)時(shí)事件通知方案,轉(zhuǎn)載需保留來源!
鄭重聲明:本文版權(quán)歸原作者所有,轉(zhuǎn)載文章僅為傳播更多信息之目的,如作者信息標(biāo)記有誤,請第一時(shí)間聯(lián)系我們修改或刪除,多謝。