理解 Comet
您可能已經聽說過 Comet,因為它最近受到了一定的關注。Comet 有時也稱反向 Ajax 或伺服器端推技術(server-side push)。其思想很簡單:將資料直接從伺服器推到瀏覽器,而不必等到瀏覽器請求資料。聽起來簡單,但是如果熟悉 Web 應用程式,尤其是 HTTP 協議,那麼您就會知道,這絕不簡單。實現 Comet 風格的 Web 應用程式,同時保證在瀏覽器和伺服器上的延展性,這隻是在最近幾年才成為可能。目前一些主流網站都有類似的原理,例如:webQQ、開心網、白社會等等,它們中訊息動態都是採用類似的技術,也許具體實現方式不一樣;
使用 Comet 的動機
HTTP 協議的成功毋庸置疑。它是 Internet 上大部分資訊交換的基礎。然而,它也有一些局限性。特別是,它是無狀態、單向的協議。請求被發送到 Web 服務器,伺服器處理請求並發回一個響應 — 僅此而已。請求必須由客戶機發出,而伺服器則只能在對請求的響應中發送資料。這至少會影響很多類型的 Web 應用程式的實用性。典型的例子就是聊天程式。另外還有一些例子,例如比賽的比分、股票行情或電子郵件程式。
HTTP 的這些局限性也是它取得一定成功的原因。請求/響應周期使它成為了經典的模型,即每個串連使用一個線程。只要能夠快速為請求提供服務,這種方法就有巨大的延展性。每秒鐘可以處理大量的請求,只需使用少量的伺服器就可以處理很大數量的使用者。對於很多經典的 Web 應用程式,例如內容管理系統、搜尋應用程式和電子商務網站等等而言,這非常適合。在以上任何一種 Web 應用程式中,伺服器提供使用者請求的資料,然後關閉串連,並釋放那個線程,使之可以為其他請求服務。如果提供初始資料之後仍可能存在互動,那麼將串連保持為開啟狀態,因此線程就不能釋放出來,伺服器也就不能為很多使用者服務。
但是,如果想在對請求做出響應並發送初始資料之後,仍然保持與使用者的互動呢?在 Web 早期,這一點常使用 meta 重新整理實現。這將自動指示瀏覽器在指定秒數之後重新裝載頁面,從而支援簡陋的輪詢(polling)。這不僅是一種糟糕的使用者體驗,而且通常效率非常低下。如果沒有新的資料要顯示在頁面上呢?這時不得不重新呈現同樣的頁面。如果對頁面的更改很少,並且頁面的大部分沒有變化呢?同樣,不管是否有必要,都得重新請求和擷取頁面上的一切內容。
Ajax 的發明和流行改變了上述狀況。現在,伺服器可以非同步通訊,因此不必重新請求整個頁面。現在可以進行增量式的更新。只需使用 XMLHttpRequest 輪詢伺服器。這項技術通常被稱作 Comet。這項技術存在一些變體,每種變體具有不同的效能和延展性。我們來看看這些不同風格的 Comet。
Comet 風格
Ajax 的出現使 Comet 成為可能。HTTP 的單向性質可以有效地加以規避。實際上有一些不同的方法可以繞過這一點。您可能已經猜到,支援 Comet 的最容易的方式是輪詢(poll)。使用 XMLHttpRequest 向伺服器發出調用,返回後,等待一段固定的時間(通常使用 JavaScript 的 setTimeout 函數),然後再次調用。這是一項非常常見的技術。例如,大多數 webmail 應用程式就是通過這種技術在電子郵件到達時顯示電子郵件的。
這項技術有優點也有缺點。在這種情況下,您期望快速返迴響應,就像任何其他 Ajax 請求一樣。在請求之間必須有一段暫停。否則,連續不斷的請求會衝垮伺服器,並且這種情況下顯然不具有延展性。這段暫停使應用程式產生一個延時。暫停時間越長,伺服器上的新資料就需要越多的時間才能到達客戶機。如果縮短暫停時間,又將重新面臨衝垮伺服器的風險。但是另一方面,這顯然是最簡單的實現 Comet 的方式。
現在應該指出,很多人認為輪詢並不屬於 Comet。相反,他們認為 Comet 是對輪詢的局限性的一個解決方案。最常見的 “真正的” Comet 技術是輪詢的一種變體,即長輪詢(long polling)。輪詢與長輪詢之間的主要區別在於伺服器花多長的時間作出響應。長輪詢通常將串連保持一段較長的時間 — 通常是數秒鐘,但是也可能是一分鐘甚至更長。當伺服器上發生某個事件時,響應被發送並隨即關閉,輪詢立即重新開始。
長輪詢相對於一般輪詢的優點在於,資料一旦可用,便立即從伺服器發送到客戶機。請求可能等待較長的時間,期間沒有任何資料返回,但是一旦有了新的資料,它將立即被發送到客戶機。因此沒有延時。如果您使用過基於 Web 的聊天程式,或者聲稱 “即時” 的任何程式,那麼它很可能就是使用了這種技術。
長輪詢有一種變體,這是第三種風格的 Comet。這通常被稱為流(streaming)。按照這種風格,伺服器將資料推回客戶機,但是不關閉串連。串連將一直保持開啟,直到到期,並導致重新發出請求。XMLHttpRequest 規範表明,可以檢查 readyState 的值是否為 3 或 Receiving(而不是 4 或 Loaded),並擷取正從伺服器 “流出” 的資料。和長輪詢一樣,這種方式也沒有延時。當伺服器上的資料就緒時,該資料被發送到客戶機。這種方式的另一個優點是可以大大減少發送到伺服器的請求,從而避免了與設定伺服器串連相關的開銷和延時。不幸的是,XMLHttpRequest 在不同的瀏覽器中有很多不同的實現。這項技術只能在較新版本的 Mozilla Firefox 中可靠地使用。對於 Internet Explorer 或 Safari,仍需使用長輪詢。
至此,您可能會想,長輪詢和流都有一個很大的問題。請求需要在伺服器上存在一段較長的時間。這打破了每個請求使用一個線程的模型,因為用於一個請求的線程一直沒有被釋放。更糟糕的是,除非要發回資料,否則該線程一直處於空閑狀態。這顯然不具有延展性。幸運的是,現代 Java Web 服務器有很多方式可以解決這個問題。搞JAVA開發的程式員,相對而言比較幸運,PHP目前還不是很多的