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

 英文原文:Reverse Ajax, Part 4: Atmosphere and CometDjavascript

 前言html

  這一系列文章展現瞭如何使用反向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。java

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

  你能夠下載本文中使用的源代碼。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支持的服務器最多。websocket

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

  圖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的處理程序。客戶端的代碼則始終是相同的;與本系列的第一、2和3部分用戶的代碼同樣(使用Comet長輪詢的聊天例子)。你有可能使用了Atmosphere的JQuery插件,但這不是必須的,由於Atmosphere並不強制使用任何的通訊協議。強烈建議你研究一下Atmosphere項目中的其餘例子,特別是用到了JSR-311註解(Jersey)的那些,它們真正地簡化了處理程序的編寫。

  清單1. AtmosphereHandler接口

 

[java]  view plain copy
 
  1. public interface AtmosphereHandler {  
  2.   void onRequest(AtmosphereResource resource)  
  3.   throws IOException;  
  4.   void onStateChange(AtmosphereResourceEvent event)  
  5.   throws IOException;  
  6.   void destroy();  
  7. }  

 

  onRequest方法接收來自客戶端的全部請求並決定是掛起仍是恢復它們(或什麼也不作),每次掛起或是恢復一個請求、發送一個廣播或是有超時發生時,就會發送一個由onStateChange方法接收的事件。

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

  清單2. AtmosphereHandler接口——onRequest

[java]  view plain copy
 
  1. Broadcaster broadcaster = BroadcasterFactory.getDefault().lookup(  
  2.   DefaultBroadcaster.class, ChatHandler.class.getName(), true);  
  3.   broadcaster.setScope(Broadcaster.SCOPE.APPLICATION);  
  4.   resource.setBroadcaster(broadcaster);  
  5.   HttpServletRequest req = resource.getRequest();  
  6.   String user = (String) req.getSession().getAttribute("user");  
  7.   if (user != null) {  
  8.     if ("GET".equals(req.getMethod())) {  
  9.       resource.suspend(-1, false);  
  10.     } else if ("POST".equals(req.getMethod())) {  
  11.       String cmd = req.getParameter("cmd");  
  12.       String message = req.getParameter("message");  
  13.     if ("disconnect".equals(cmd)) {  
  14.       close(resource);  
  15.     } else if (message != null && message.trim().length() > 0) {  
  16.       broadcaster.broadcast("[" + user + "] " + message);  
  17.     }  
  18.   }  
  19. }  

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

  清單3. AtmosphereHandler接口——onStateChange

[java]  view plain copy
 
  1. Broadcaster broadcaster = BroadcasterFactory.getDefault().lookup(  
  2.   DefaultBroadcaster.class, ChatHandler.class.getName(), true);  
  3.   // Client closed the connection.  
  4.   if (event.isCancelled()) {  
  5.     close(event.getResource());  
  6.     return;  
  7.   }  
  8.   try {  
  9.     String message = (String) event.getMessage();  
  10.     if (message != null) {  
  11.       PrintWriter writer =  
  12.       event.getResource().getResponse().getWriter();  
  13.       writer.write(message);  
  14.       writer.flush();  
  15.     }  
  16.   } finally {  
  17.     if (!event.isResumedOnTimeout()) {  
  18.       event.getResource().resume();  
  19.   }  
  20. }  

  如今你已經具有了用來運做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 六、7和8中,以及任何支持Servlet 3.0規範的服務器中。能夠經過與擴展同樣的方式來增長和開發傳輸,你應該可以編寫傳輸來支持Grizzly WebSocket API及其餘的一些協議,而後在配置CometD服務器的相關步驟中加入它們。圖2給出了主要的CometD塊的一個概覽。

  圖2. CometD的架構概覽

  圖2並未給出訪問消息信道的安全層。

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

  清單4. web.xml

[html]  view plain copy
 
  1. <servlet>  
  2. <servlet-name>cometd< /servlet-name>  
  3. <servlet-class>  
  4. org.cometd.java.annotation.AnnotationCometdServlet  
  5. </servlet-class>  
  6. <async-supported>true< /async-supported>  
  7. [...]  
  8. <init-param>  
  9. <param-name>services< /param-name>  
  10. <param-value>ChatService< /param-value>  
  11. </init-param>  
  12. <init-param>  
  13. <param-name>transports< /param-name>  
  14. <param-value>  
  15. com.ovea.cometd.websocket.jetty8.Jetty8WebSocketTransport  
  16. </param-value>  
  17. </init-param>  
  18. </servlet>  

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

  清單5. CometD ChatService

[java]  view plain copy
 
  1. @Service  
  2. public final class ChatService {  
  3.   
  4. @Inject  
  5. BayeuxServer server;  
  6.   
  7. @PostConstruct  
  8. void init() {  
  9. server.addListener(new BayeuxServer.SessionListener() {  
  10. @Override  
  11. public void sessionAdded(ServerSession session) {  
  12. [...]  
  13. }  
  14.   
  15. @Override  
  16. public void sessionRemoved(ServerSession session, boolean timedout) {  
  17. [...]  
  18. }  
  19. });  
  20. }  
  21.   
  22. @Configure("/**")  
  23. void any(ConfigurableServerChannel channel) {  
  24. channel.addAuthorizer(GrantAuthorizer.GRANT_NONE);  
  25. }  
  26.   
  27. @Configure("/chatroom")  
  28. void configure(ConfigurableServerChannel channel) {  
  29. channel.addAuthorizer(new Authorizer() {  
  30. @Override  
  31. public Result authorize(  
  32. [..] // check that the user is in session  
  33. }  
  34. });  
  35. }  
  36.   
  37. @Listener("/chatroom")  
  38. void appendUser(ServerSession remote,  
  39. ServerMessage.Mutable message) {  
  40. [...]  
  41. }  
  42. }  

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

  1. 依賴注入

  2. 生命週期管理

  3. 全局信道配置

  4. 安全管理

  5. 消息轉換(把用戶名稱添加到全部消息以前)

  6. 會話管理

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

  清單6. CometD的客戶端代碼

[javascript]  view plain copy
 
  1. // 先建立cometd對象並配置它  
  2. var cometd = new $.Cometd('CometD chat client');  
  3. cometd.configure({  
  4. url: document.location + 'cometd',  
  5. logLevel: 'debug'  
  6. });  
  7. cometd.websocketEnabled = 'WebSocket' in window;  
  8.   
  9. // 而後註冊一些監聽器。 說明信道 (有着  
  10. // /meta/格式的那些是具體的預留通道)  
  11. cometd.addListener('/meta/disconnect', function(message) {  
  12. [...]  
  13. });  
  14.   
  15. cometd.addListener('/meta/connect', function(message) {  
  16. [...]  
  17. });  
  18.   
  19. // 而後啓動一個可使用的鏈接:  
  20. cometd.handshake();  
  21.   
  22. // 而後訂閱:  
  23. cometd.subscribe('/chatroom', function(event) {  
  24. [...] // event.data存放消息  
  25. });  
  26.   
  27. // 咱們最終以這種方式來發送數據給聊天室:  
  28. 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的支持。

  代碼下載

  reverse_ajaxpt4_source.zip

相關文章
相關標籤/搜索