反向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

相关文章
相关标签/搜索