反向Ajax,第4部分:Atmosphere和CometD )

來源:互聯網
上載者:User

前言

這一系列文章展示了如何使用反向Ajax技術開發事件驅動的web應用,第1部分內容介紹了反向Ajax(Reverse Ajax)、polling(輪詢)、streaming(流)、Comet和長輪詢(long polling);第2部分內容介紹了如何使用WebSocket來實現反向Ajax,並討論了使用Comet和WebSocket的web伺服器的局限性;第3部分內容說明的是,如果需要支援多種伺服器或是為使用者提供一個部署在他們自己的伺服器上的獨立web應用的話,實現自己的Comet或是WebSocket通訊系統會存在一些難處。即使用戶端的JavaScript代碼很簡單,但你需要用到一些異常處理、重串連和確認功能。在伺服器端,全域性API的缺失和多種web伺服器API導致了對架構的需求,這帶來了一層抽象,第3部分內容還談到了Socket.IO。

在本文中,我們瞭解Atmosphere和CometD,它們是最廣為人知的Java伺服器的開源反向Ajax庫。

你可以下載本文中使用的原始碼。

前提條件

理想情況下,要充分體會本文的話,你應該對JavaScrpit和Java有一定的瞭解。若要運行本文中的例子,你還需要最新版本的Maven和JDK。

Atmosphere架構

Atmosphere是一個Java技術架構,其提供了通用的API來使用許多web伺服器的Comet和WebSocket,這些web伺服器包括了Tomcat、Jetty、GlassFish、Weblogic、Grizzly、JBossWeb、JBoss和Resin,其還支援任何支援Servlet 3.0規範的web伺服器。在本系列文章提到的各個架構中,Atmosphere支援的伺服器最多。

Atmosphere可以檢測本地化的伺服器端API(針對Comet和WebSocket),對於Comet來說,如果可用的話,就切換回Servlet3.0;或者,依然是針對Comet,其會回退到一種“受管”的非同步模式中(但沒有達到Jetty Continuation的那種延展性)。Atmosphere的存在已經超過了兩年的時間,現在依然在處在活躍的發展階段。其被用在大型的web應用中,比如說JIRA,這是一個最有名的問題追蹤器。圖1給出了Atmosphere的架構。

圖1. Atmosphere的架構一覽

Atmosphere由Atmosphere運行時組成,其為所有不同的web伺服器解決方案和標準提供了一個通用的API。在這之上,用戶端可以設定一個簡單的servlet來通過Google Web Toolkit(GWT)訪問該API和反向Ajax功能。或者,你也可以使用Jersey,一個實現了JSR-311(JAX-RS規範)的架構。有了所提供的額外註解,因此Atmosphere可用在RESTful服務中。在配置了所選擇的模組後,你就可以通過實現一些類來訪問Atomsphere運行時(本文稍後會討論到)。你還可以選擇使用一些提供的外掛程式,這些外掛程式增加了對叢集、訊息、依賴注入等的支援。如果你正在使用一個web架構(Wecket、Struts、Spring
MVC)的話,則可以使用Atmosphere的MeteorServlet來透明地添加反向Ajax支援。這一Servlet暴露出一個Meteor對象,該對象可在你的控制器內部檢索到,用來掛起或是恢複請求。

Atmosphere的強大停留在伺服器端:其提供一個了標準的API,該API覆蓋了所有與WebSocket或是Comet通訊的不同解決方案和方法。Atmosphere並未用到用戶端和伺服器端之間的協議,比如說Socket.IO和CometD等,這兩種庫都提供了一個用戶端的JavaScript和一個伺服器端的servlet,它們的通訊用到了一種特定的協議(握手、訊息、確認和心跳)。Atmosphere的目標是在伺服器端提供一種通用的通訊通道。如果你需要用到某種特定協議的話,比如說Bayeux(CometD用到的一個協議),就需要在Atmosphere中開發自己的“處理常式”。CometD外掛程式就是這樣做的:其利用了Atmosphere的API來掛起和恢複請求,並委託CometD的類來管理使用了Bayeux協議的CometD通訊。

Atmosphere所帶的JQuery用戶端庫方便了串連的建立,其能夠自動檢測最好的可用傳輸方式(WebSocket或是CometD)。Atmosphere的jQuery外掛程式的用法類似於HTML5 WebSocket API,首先你串連到伺服器端,註冊一個回調來接收資訊,然後就可以推一些資料了。

本文中的原始碼包含了一個Atmosphere例子,該類直接用到了一個使用Atmosphere servlet的處理常式。用戶端的代碼則始終是相同的;與本系列的第1、2和3部分使用者的代碼一樣(使用Comet長輪詢的聊天例子)。你有可能使用了Atmosphere的JQuery外掛程式,但這不是必須的,因為Atmosphere並不強制使用任何的通訊協定。強烈建議你研究一下Atmosphere項目中的其他例子,特別是用到了JSR-311註解(Jersey)的那些,它們真正地簡化了處理常式的編寫。

清單1. AtmosphereHandler介面

public interface AtmosphereHandler {  void onRequest(AtmosphereResource resource)  throws IOException;  void onStateChange(AtmosphereResourceEvent event)  throws IOException;  void destroy();}

onRequest方法接收來自用戶端的所有請求並決定是掛起還是恢複它們(或什麼也不做),每次掛起或是恢複一個請求、發送一個廣播或是有逾時發生時,就會發送一個由onStateChange方法接收的事件。

Comet聊天例子的onRequest方法實現如清單2所示。

清單2. AtmosphereHandler介面——onRequest

Broadcaster broadcaster = BroadcasterFactory.getDefault().lookup(  DefaultBroadcaster.class, ChatHandler.class.getName(), true);  broadcaster.setScope(Broadcaster.SCOPE.APPLICATION);  resource.setBroadcaster(broadcaster);  HttpServletRequest req = resource.getRequest();  String user = (String) req.getSession().getAttribute("user");  if (user != null) {    if ("GET".equals(req.getMethod())) {      resource.suspend(-1, false);    } else if ("POST".equals(req.getMethod())) {      String cmd = req.getParameter("cmd");      String message = req.getParameter("message");    if ("disconnect".equals(cmd)) {      close(resource);    } else if (message != null && message.trim().length() > 0) {      broadcaster.broadcast("[" + user + "] " + message);    }  }}

一種典型的習慣做法是掛起GET請求並使用POST請求來發送訊息。在接收到訊息時,該訊息被廣播給所有在廣播器內進行了註冊的資源。可以注意到,該例子並未往HttpServlet輸出資料流中寫入任何東西,廣播或是掛起行為只是發送由其他實現方法接收的事件,如清單3所示:

清單3. AtmosphereHandler介面——onStateChange

Broadcaster broadcaster = BroadcasterFactory.getDefault().lookup(  DefaultBroadcaster.class, ChatHandler.class.getName(), true);  // Client closed the connection.   if (event.isCancelled()) {    close(event.getResource());    return;  }  try {    String message = (String) event.getMessage();    if (message != null) {      PrintWriter writer =      event.getResource().getResponse().getWriter();      writer.write(message);      writer.flush();    }  } finally {    if (!event.isResumedOnTimeout()) {      event.getResource().resume();  }}

現在你已經具備了用來運作Camet聊天例子的所有所需,概括來說,Atmosphere的一些重要概念是:資來源物件描述串連,廣播器負責觸發資源事件並決定何時掛起或是恢複一個請求。需要注意的是,該例子只適用於Comet,若要能夠使用WebSocket和Comet兩者的話,應該要使用某種用戶端庫,且需要一個更複雜的處理常式。

表1列出了使用Atmosphere架構的利弊。

表1. Atmosphere的優點和缺點

1. 優點

如果需要把web應用部署在你不能自己決定的幾種web伺服器上,那麼因為Atmosphere支援許多種web伺服器,所以你的應用的反向Ajax功能能夠正確工作的機會大大增加。

在沒有定義了任何協議的原始的反向Ajax通訊之上,因為想要開發或是擴充它,這時你會需要一個通用的API。

2. 缺點

缺乏關於Atmosphere的架構、項目、概念和API的文檔,如果需要深入原始碼或是分析一些提供的例子的話,這些文檔很有協助。相比於諸如Socket.IO和CometD一類的其他架構的簡單API來說,其API的技術性很強,有些很晦澀。即使是在使用Atmosphere的註解時,某些名稱和屬性也過於專業了。

雖然在伺服器端有很好的抽象,但卻沒有一個很好的用戶端庫。因為沒有協議,故所有的其他功能都留給了開發人員來實現。對於一個大的、可伸縮的web應用來說,如果你需要進階的時間檢測、確認、回退、跨域等功能的話,特別是在行動裝置上運行時,那麼目前的庫就太過簡單了。在這種情況下,CometD更為可靠一些;其利用了一個能夠用來啟用某些控制流程和錯誤偵測的通訊協定,所有的這些都在CometD內部提供。如果你需要額外的功能的話,使用Atomsphere CometD外掛程式所帶的CometD JavaScript用戶端是另一個不錯的選擇。

CometD架構

CometD架構是一個已經存在了好幾年的基於HTTP的事件驅動的通訊解決方案,其版本2增加了對註解配置和WebSocket的支援。CometD架構提供了一個Java伺服器端的部分和一個Java用戶端的部分,以及基於JQuery和Dojo的JavaScript用戶端庫。CometD使用了一個被稱作Bayeux的標準的通訊協定,允許你啟用訊息確認、流程式控制制、同步以及叢集等的某些擴充。

CometD的事件驅動方法非常適合事件驅動的web開發這一新概念,和傳統的案頭使用者介面一樣,所有的組件通訊通過一個匯流排來發送通知和接收事件,因此所有的通訊都是非同步。

CometD架構:

1. 有詳盡的文檔說明

2. 提供了例子和Maven原型來方便項目的啟動

3. 提供了一個精心設計的API來支援擴充開發

4. 提供了被稱為Oort的叢集模組,該模組提供了在一個叢集中把多個CometD web伺服器當作節點來啟動並執行能力,在其之前是一個負載平衡器調節大資料量的HTTP串連。

5. 支援細粒度的安全性原則配置,我們可以指定通過哪一個通道來發送訊息。

6. 很好地整合了Spring和Google Guice(依賴注入架構)

Bayeux協議

Byeux通訊協定主要是通過HTTP來實現的,其在用戶端和伺服器端之間以非同步方式提供一個響應式的雙向通訊。Bayeux協議以訊息路由的通道為基礎,從用戶端向伺服器端傳遞,從伺服器端向用戶端傳遞,或是用戶端之間傳遞(但要通過伺服器端),Bayeux是一種發布-訂閱式的協議。CometD實現了Bayeux協議,從而在Comet和WebSocket傳輸之上提供了一個抽象層來通過Bayeux路由請求。

伺服器端及其內部

CometD捆綁了三種傳輸:JSON、JSONP和WebSocket,它們依賴於Jetty Continuation和Jetty WebSocket API。預設情況下,CometD可用在Jetty 6、7和8中,以及任何支援Servlet 3.0規範的伺服器中。可以通過與擴充一樣的方式來增加和開發傳輸,你應該能夠編寫傳輸來支援Grizzly WebSocket API及其他的一些協議,然後在配置CometD伺服器的相關步驟中加入它們。圖2給出了主要的CometD塊的一個概覽。

圖2. CometD的架構概覽

圖2並未給出訪問訊息通道的安全層。

本文提供的原始碼包括了一個使用了CometD的web應用,這一web應用的描述符包含了清單4中的這一聊天例子的定義。

清單4. web.xml

<servlet> <servlet-name>cometd< /servlet-name> <servlet-class> org.cometd.java.annotation.AnnotationCometdServlet</servlet-class> <async-supported>true< /async-supported> [...]<init-param> <param-name>services< /param-name> <param-value>ChatService< /param-value> </init-param> <init-param> <param-name>transports< /param-name> <param-value> com.ovea.cometd.websocket.jetty8.Jetty8WebSocketTransport</param-value> </init-param> </servlet>

CometD這一servlet支援控制全域設定的多個選項,比如說設定傳輸和服務的能力。在該例子中,假設你想要增加Jetty 8的WebSocket支援的話,則伺服器端的CometD服務類ChatService會控制每個人都會在其中發言的聊天室,如清單5所示:

清單5. CometD ChatService

@Servicepublic final class ChatService {@InjectBayeuxServer server;@PostConstructvoid init() {server.addListener(new BayeuxServer.SessionListener() {@Overridepublic void sessionAdded(ServerSession session) {[...]}@Overridepublic void sessionRemoved(ServerSession session, boolean timedout) {[...]}});}@Configure("/**")void any(ConfigurableServerChannel channel) {channel.addAuthorizer(GrantAuthorizer.GRANT_NONE);}@Configure("/chatroom")void configure(ConfigurableServerChannel channel) {channel.addAuthorizer(new Authorizer() {@Overridepublic Result authorize([..] // check that the user is in session }});}@Listener("/chatroom")void appendUser(ServerSession remote,ServerMessage.Mutable message) {[...]}}

清單5說明了CometD的一些主要特徵,其中包括:

1. 依賴注入

2. 生命週期管理

3. 全域通道配置

4. 安全管理

5. 訊息轉換(把使用者名稱稱添加到所有訊息之前)

6. 會話管理

在用戶端,該例子不會啟用任何的擴充——只是原始的CometD代碼,如清單6所示:

清單6. CometD的用戶端代碼

// 先建立cometd對象並配置它 var cometd = new $.Cometd('CometD chat client');cometd.configure({url: document.location + 'cometd',logLevel: 'debug'});cometd.websocketEnabled = 'WebSocket' in window;// 然後註冊一些監聽器。 說明通道 (有著// /meta/格式的那些是具體的預留通道) cometd.addListener('/meta/disconnect', function(message) {[...]});cometd.addListener('/meta/connect', function(message) {[...]});// 然後啟動一個可以使用的串連: cometd.handshake();// 然後訂閱: cometd.subscribe('/chatroom', function(event) {[...] // event.data存放訊息 });// 我們最終以這種方式來發送資料給聊天室: cometd.publish('/chatroom', msg);

CometD的用戶端API很容易使用和理解,同時又保留了強大的功能和可擴充性。本文只是涵蓋了web應用的主要部分,因此可以通過研究例子應用來更好地瞭解CometD的強大功能。

表2列出了使用CometD架構的利弊。

表2. CometD的優點和缺點

1. 優點

從用戶端到伺服器端,以及從一個獨立的Java用戶端到伺服器端,CometD提供了一個完整的解決方案。架構有詳盡的文檔說明,有一個很好的API,且非常容易使用。最重要的是,它擁有一種事件驅動的方法。CometD和Bayeux是許多事件驅動應用的構成部分,其他的反向Ajax架構並未提供任何的事件驅動機制,使得終端使用者不得不開發自己的定製解決方案。

CometD支援許多必需的功能,比如說重串連、可靠的逾時檢測、回退、批處理、訊息確認,以及更多你不會在其他反向Ajax架構中找得到的功能。CometD可讓你實現最可靠的、低延時的通訊。

2. 缺點
除了Jetty for Comet(Tomcat)之外,CometD目前並未支援任何的Servlet 2.5容器,其也不支援Glassfish/Grizzly WebSocket。

 結束語

Atmosphere和CometD都是穩定的、開源的反向Ajax解決方案,我們在Ovea選用的是CometD,因為我們在一個叢集環境內部為行動裝置開發可伸縮的事件驅動應用,我們完全掌控基礎設施(我們使用Jetty)。不過,在沒有額外開發的情況下,如果你正在出售web應用且希望你的反向Ajax功能在儘可能多的伺服器上都可運作的話,CometD可能不是最好的選擇。但現在隨著越來越多的Web應用開始支援Servlet 3.0規範,CometD的局限性呈下降趨勢。說到傳輸層,餘下的主要差異則取決於對WebSocket的支援。
轉自:http://select.yeeyan.org/view/213582/217948

代碼下載

  reverse_ajaxpt4_source.zip

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.