天天看點

伺服器推送技術 1.使用 Java 實作 Comet 風格的 Web 應用

伺服器推送技術是最近在項目中有可能要用到的一項技術,是以提前研究了一下。

考慮這樣一個場景,在各類WEB應用中,如果有其他使用者更改了某位使用者正在關注的任何消息,則使用者希望得到通知。如果一個 Web 站點顯示動态資料,如股價等,那麼所有使用者都必須立即得到關于變更的通知。

這些場景本身屬于一類稱為 “伺服器推送” 的問題。通常,伺服器是中心實體,伺服器将首先獲得關于所發生的任何更改的通知,伺服器負責将此類更改通知所有連接配接的用戶端。但遺憾的是,HTTP 是用戶端-伺服器通信的标準協定,它是無狀态的,而且在某種意義上來說,也是一種單向的協定。HTTP 場景中的所有通信都必須由用戶端發起,至伺服器結束,然而我們所提到的場景的需求則完全相反。對于伺服器推送來說,需要由伺服器發起通信,并向用戶端發送資料。HTTP 協定并無相關配置,Web 站點應用程式開發人員使用獨創的方法來繞過這些問題,

目前來說,伺服器推送(PUSH)機制大緻分為幾種:

1. 短輪詢

用戶端以固定(或可配置)的時間間隔與伺服器聯系,查找是否有新的更新可用。在大多數時候,這些輪詢純粹是浪費,因為伺服器沒有任何更新。這種方法不是沒有代價的,它有兩大主要問題:

首先,這種方法非常浪費網絡資源,

其次,因為輪詢有一定的時間間隔,是以用戶端不會獲得實時的更新。

2. 長輪詢

長輪詢是用于更新伺服器資料的另外一種方法。這種方法的理念就是用戶端建立連接配接,伺服器阻塞連接配接(通過使請求線程在某些條件下處于等待狀态),有資料可用時,伺服器将通過阻塞的連接配接發送資料,随後關閉連接配接。用戶端在接收到更新後,立即重建立立連接配接,伺服器重複上述過程,以此實作近于實時的通信。然而,長輪詢具有以下缺陷:

一般的浏覽器預設允許每台伺服器具有兩個連接配接。在這種情況下,一個連接配接始終是繁忙狀态。因而,UI 隻有一個連接配接(也就是說,能力減半)可用于為使用者請求提供服務。這可能會導緻某些操作的性能降低。

仍然需要打開和關閉 HTTP 連接配接,如果采用的是非持久連接配接模式(keepAlive=false),那麼這種方法的代價可能極高。

這種方法近于實時,但并非真正的實時。(當然,某些外部因素總是不可控的,比如網絡延時,在任何方法中都會存在這些因素。)

3. 流傳送

流通道(streaming channel)與長輪詢大緻相同,差别在于伺服器不會關閉響應流。而是特意保持其處于打開狀态,使浏覽器認為還有更多資料即将到來。但是,流通道也有着自己的缺陷:

最大的問題就是資料重新整理(flushing)。

如果發現套接字将打開較長的時間,某些浏覽器實作可能會自行決定關閉套接字。在這種情況下,通道需要重建立立。

通常,第一個問題可通過為每個流響應附加垃圾有效載荷來解決,使響應資料足以填滿緩沖區。第二個問題可通過 “保持活動” 或按固定間隔 “同步” 消息來欺瞞浏覽器,使浏覽器認為資料是以較慢的速率傳入的。

下面介紹一些常用的實作推送的技術。

1. Comet

Comet 有時也稱反向 Ajax 或伺服器端推技術(server-side push)。其思想很簡單:将資料直接從伺服器推到浏覽器,而不必等到浏覽器請求資料。聽起來簡單,但是如果熟悉 Web 應用程式,尤其是 HTTP 協定,那麼您就會知道,這絕不簡單。實作 Comet 風格的 Web 應用程式,同時保證在浏覽器和伺服器上的可伸縮性,這隻是在最近幾年才成為可能。

對于 Apache Tomcat,若要使用 Comet,主要需要做兩件事。首先,需要對 Tomcat 的配置檔案 server.xml 稍作修改。預設情況下啟用的是更典型的同步 IO 連接配接器。現在隻需将它切換成異步版本,如下 所示。

<!-- This is the usual Connector, comment it out and add the NIO one --> <!-- Connector URIEncoding="utf-8" connectionTimeout="20000" port="8084" protocol="HTTP/1.1" redirectPort="8443"/ --> <Connector connectionTimeout="20000" port="8080" protocol="org.apache. coyote.http11.Http11NioProtocol" redirectPort="8443"/>

然後,建立一個實作 org.apache.catalina.CometProcessor 接口的 servlet。

CometProcessor

接口要求實作

event

方法。這是用于 Comet 互動的一個生命周期方法。Tomcat 将使用不同的

CometEvent

執行個體調用。通過檢查

CometEvent

eventType

,可以判斷正處在生命周期的哪個階段。當請求第一次傳入時,即發生

BEGIN

事件。

READ

事件表明資料正在被發送,隻有當請求為

POST

時才需要該事件。遇到

END

ERROR

事件時,請求終止。

具體的例子,請參照這個位址。

而目前常用的Comet架構有:

CometD:是一個Dojo Fondation的項目,它提供了Bayeux protocol的javascript、java、perl、python及其他語言的實作。在CometD的站點裡同時提供了Sun,IBM,BEA等公司的Comet的實作的産品的連結。

2. HTML5

HTML5提供了兩種符合W3C标準的推送方式:SSE和Web Socket。

先介紹一下SSE(Server-sent-envets),以PHP服務端為例,

客戶通路的頁面是

sse.htm

<!DOCTYPE html> <html> <body> <script> var source = new EventSource('source.php'); source.onmessage = function(event){ var text = event.data; window.webkitNotifications.createNotification('', 'Alert', text).show(); } </script> </body> </html>

服務端推送消息的腳本是

source.php

header("Content-Type: text/event-stream"); header("Cache-Control: no-cache"); mysql_connect("localhost", "user", "pass"); mysql_select_db("eventstream"); $q = mysql_query("select textnotif from notification where read='0'"); $r = mysql_fetch_array($q); $notif = $r[textnotif]; if($notif != ""){ echo "data: ".$notif.PHP_EOL; }

SSE實作了從伺服器到用戶端的單向推送消息的功能,目前浏覽器Chrome,Safari都可以支援。具體還有哪些浏覽器支援,可以點這裡檢視。

而WEB Socket提供了一個雙向的消息通道。WebSocket 是通過 HTTP 協定的初始握手階段然後更新到 Web Socket 協定以支援實時資料通信。WebSocket 協定設計了更為輕量級的 Header。

這裡可以參考到一個使用WebSocket技術的執行個體。而目前網上關于WebSocket的内容還是比較多的。

由于WebSocket具有雙向溝通的優勢,是以可以實作聊天室、遊戲、股票交易等等需要雙向通訊的應用上。而SSE雖然隻能實作從伺服器向用戶端的單向推送,但是它可以自動重新連結,具備EventID等優勢,是以也有用武之地。

而另外一個可作參考的輕量級的伺服器推送架構是Pushlets,它提供了從HTTP Push到DHTML,以及Pushlets架構的具體實作執行個體。

參考:

1.使用 Java 實作 Comet 風格的 Web 應用

2.應用 HTML5 的 WebSocket 實作 BiDirection 資料交換 3.Pushlets