本 系列 文章向您展示如何使用反向 Ajax 技术开发事件驱动的 Web 程序。第 1 部分 介绍了 Reverse Ajax、轮询、流、Comet 和长轮询。第 2 部分 介绍了如何使用 WebSocket 实现 Reverse Ajax,还讨论了使用 Comet 和 WebSocket 的 Web 服务器的限制。第 3 部分 探讨了当您需要支持多个服务器或 提供一个用户可以自己的服务器上部署的独立 Web 应用程序时,您实现自己的 Comet 或 WebSocket 通信系统的过程中会遇到的一些困难。即使客户端上的 JavaScript 代码很简单,您仍然需要一些异常处理、重新连接和确认功能。在服务器端,由于缺少全局 API 以及一些 Web 服务器 API,导致需要使用附带抽象功能的框架。第 3 部分还讨论了Socket.IO。
在本文中,您将了解 Atmosphere 和 CometD,它们是用于 Java 服务器的最有名的开源反向 Ajax 库。
先决条件
在理想的情况下,如果想最大限度地利用本文,您应该了解 JavaScript 和 Java。要运行本文中的示例,则需要使用最新版的 Maven 和 JDK。
Atmosphere 框架
Atmosphere 是一种 Java 技术框架,它提供了一个通用 API,以便使用许多 Web 服务器(包括 Tomcat、Jetty、GlassFish、Weblogic、Grizzly、JBossWeb、JBoss 和 Resin)的 Comet 和 WebSocket 特性。它支持任何支持 Servlet 3.0 Specification 的 Web 服务器。在本系列文章展示的所有框架中,Atmosphere 支持的服务器最多。
Atmosphere 可以针对 Comet,Atmosphere 检测本机服务器 API 并切换回 Servlet 3.0 的运行时环境。或者将退回到 "托管" 异步模式(但不具有 Jetty Continuations 的可伸缩性),这也适用于 Comet。Atmosphere 已面市两年多了,仍然处于积极开发阶段。它被应用于大型 Web 应用程序中,比如 JIRA,这是最有名的问题追踪器之一。图 1 显示了 Atmosphere 框架。
图 1. Atmosphere 的构架视图
Atmosphere 框架由 Atmosphere 运行时所组成,为各种不同 Web 服务器解决方案和标准提供了一个通用 API。在运行时上面,客户端可以通过设置 servlet 并使用 Google Web Toolkit (GWT) 来访问 API 和反向 Ajax 特性。或者,您还可以使用 Jersey,这是一种用于实现 JSR-311(JAX-RS 规范)的框架。因此,可以使用注释的方式将 Atmosphere 用于更多其它的 REST 场景中。配置您选择的模块之后,就可以通过实现一些类(本文稍后进行讨论)来访问 Atmosphere 运行时。您还可以随意使用所提供的插件,添加对集群、消息传递、依赖注入的支持。如果您正在使用一个 Web 框架(Wicket、Struts 或 Spring MVC),您可以通过使用 Atmosphere 的 MeteorServlet 明确添加对反向 Ajax 的支持。该 servlet 展示了Meteor 对象,可以在控制器和服务中检索该对象,以便挂起或重新开始请求。
Atmosphere 的优势仍然在于服务器端:它提供了一个标准化 API,该 API 包含与 WebSockets 或 Comet 进行通信的所有不同的解决方案和方法。Atmosphere 没有使用客户端和服务器之间通信的协议,如 Socket.IO 和 CometD。这两个库均提供了客户端 JavaScript 和服务器端 servlet,使用特定的协议(用于握手、消息收发、确认和心跳的协议)进行通信。Atmosphere 的目标是在服务器端提供一个通用的通信信道。如果需要使用特定的协议,比如 Bayeux(CometD 使用的协议),则必须在 Atmosphere 中开发您自己的 “处理程序”。CometD 插件可以满足您的需求:它利用 Atmosphere 的 API 来挂起和重新开始请求,并通过使用 Bayeux 协议来委托 CometD 类来管理 CometD 通信。
Atmosphere 提供了一个 jQuery 客户端库,该库可以使连接设置变得更容易,它能够自动检测可以使用的最佳传输协议(WebSockets 或 CometD)。Atmosphere 的 jQuery 插件的用法与 HTML5 WebSockets API 相似。首先要连接到服务器,注册一个回调来接收消息,然后再放入一些数据。
本文所提供的 源代码 中包含了一个 Atmosphere 样例,该样例直接将处理程序用于 Atmosphere servlet。客户端代码始终保持不变;与本 系列 第 1 部分、第 2 部分、第 3 部分文章中所用的代码相同(都位于使用 Comet 长轮询的聊天样例代码中)。您还可以使用 Atmosphere 的 jQuery 插件,但这并不是必需的,因为 Atmosphere 不强迫使用任何通信协议。强烈建议您查看 Atmosphere 项目中的其他示例,特别是那些使用 JSR-311 注释 (Jersey) 的示例。它们确实简化了处理程序的编写。
清单 1 显示了 Atmosphere 处理程序的接口。
清单 1. AtmosphereHandler 接口
public interface AtmosphereHandler<F, G> { void onRequest(AtmosphereResource<F, G> resource) throws IOException; void onStateChange(AtmosphereResourceEvent<F, G> event) throws IOException; void destroy(); }
onRequest 方法接收到来自客户端的所有请求,并决定是否挂起或重新开始请求(或不执行任何操作)。每次挂起或重新开始一个请求、发送一条广播或者出现超时,都会通过 onStateChange 方法发送和接收一个事件。
清单 2 中显示了 Comet 聊天应用程序的 onRequest 方法的实现。
清单 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);
}
}
}
发表回复