1、浅析 Comet 技术在 Java Web 实时系统开发中的应用摘要:Comet 技术通过实现服务器推(server push)来解决 AJAX 需要定时频繁发送请求的问题,从而给 Web 实时系统带来了全新的交互性。本文分析了 Comet 技术的两种实现方式:长轮询方式(long-polling)和流方式(streaming),并通过一个服务器推送随即数的实例,展示了使用Comet 技术开发 Java Web 实时系统的开发方法和步骤。 关键词:Comet;AJAX;服务器推送;Web 实时系统 HTTP 协议是互联网上大量信息交换的基础,其特点是,它是基于请求响应模式的无状态的单向协议,即
2、必须由客户端发起一个请求建立连接,服务器接收请求,把数据返回给客户端,然后释放连接。下一次,再由客户端发起另一次请求,重复上述过程。服务器始终处于“被动”地位。 HTTP 协议这一特点,既成就了它的成功,也导致了它的局限性。服务器处理请求的经典模式是一个线程处理一个连接,结束之后,关闭该连接,释放线程以服务于其他请求。只要响应速度足够快,那么我们可以以相对较少的服务器为数量庞大的用户提供服务。这非常适合于传统的 Web 应用,比如:搜索引擎、内容管理系统和电子商务网站等。然而,这种方式并不能满足有实时性要求的应用的需求,很多应用都需要服务器能实时地将更新的信息传送到客户端,而无须客户端发出请求
3、。例如,新闻标题、证券报价和拍卖行情等。 在 Web 的早期,人们通过在 HTML 头部加入 META 元信息来实现 HTML自动刷新。该标记指示浏览器每隔一定的时间间隔刷新一次页面。这不仅带来糟糕的用户体验,而且是一种低效的做事方式。因为如果没有新的数据,该页面就没必要刷新;如果页面只存在小范围内的变化,该页面就没有必要全部刷新。 AJAX(Asynchronous JavaScript and XML,异步 JavaScript 和 XML)的出现改变了上述情况。Ajax 的工作原理相当于在客户和服务器之间加了个中间层,使客户请求与服务器响应异步化。并不是所有的请求都提交给服务器,像些数据
4、验证和数据处理等都交给 AJAX 引擎自己来做,只有确定需要从服务器读取新数据时再由 AJAX 引擎代为向服务器提交请求。使用 Ajax 的最大优点就是能在不刷新整个页面的前提下维护数据,使得Web 应用程序更为迅捷地响应用户交互,并避免了在网络上发送那些没有改变的信息。然而,AJAX 仍然受限于 Web 请求/响应模式的弱点,使得服务器不能推送实时动态的 Web 数据。 1 Comet 技术实现方式1 Comet 技术被称为反 AJAX(Reverse AJAX)技术,它通过实现服务器推(server push)来解决 AJAX 需要定时频繁发送请求的问题。通过 Comet,客户端所需要的响
5、应信息不再需要主动地去索取,而是在服务器端以事件(Event)的形式推至客户端。 Comet 技术的实现方式有两种:长轮询方式(long-polling)和流方式(streaming)。 长轮询:HTTP 的连接保持,服务器端会阻塞请求,直到服务器端有一个事件触发或者到达超时。客户端在收到响应后再次发出请求,重新建立连接。通过这种方式,服务器可以在数据可用的任何时候将数据“推”到客户端。因为这种方案基于 AJAX,请求异步发出,无须安装插件,IE、Mozilla FireFox 都支持。 流方式:在流方式中,服务器推数据返回客户端,但不关闭连接,连接始终保持,直到超时,超时后通知客户端重新建立
6、连接,并关闭原来的连接。在长轮询方式下,客户端是在 XMLHttpRequest 的 readystate 为 4(即数据传输结束)时调用回调函数,进行信息处理。当 readystate 为 4 时,数据传输结束,连接已经关闭。Mozilla Firefox 提供了对流方式的支持,即 readystate 为 3 时(数据仍在传输中),客户端可以读取数据,从而无须关闭连接,就能读取处理服务器端返回的信息。IE 在 readystate 为 3 时,不能读取服务器返回的数据,目前 IE 不支持流方式。 不管是长轮询还是流,请求都需要在服务器上存在一段较长时间,因此 Comet 被称为“基于 HT
7、TP 长连接的服务器推技术” 。这打破了每个请求一个线程的模型。这个模型显然对 Comet 不适用。Java 对此提出了非阻塞 IO(non-blocking IO)解决方案,Java 通过它的 NIO 库提供非阻塞 IO处理 Comet。 传统的阻塞式 IO,每个连接必须要开一个线程来处理,您始终从一个线程中读取流直到整个流完成,然后关闭连接。因此阻塞式 IO 对大量并发的短生命周期连接不会造成问题。而非阻塞 IO 处理连接是异步的。当某个连接发送请求到服务器,服务器把这个连接请求当作一个请求“事件”,并把这个“事件”分配给相应的函数处理。我们把这个处理函数放到线程中去执行,执行完就把线程归
8、还。这样一个线程就可以异步地处理多个事件。 为了获得事件通知,我们需要一个机制,它只在需要读时才读,需要写时才写,但又保持连接打开以迅速响应发生的事件。为了方便这个过程,就要用到 NIO,它已是 1.4 版本以后的 Java 语言的一部分。 2 使用 Java 开发 Comet 风格的 Web 应用 支持 Comet 的 Java Web 开源服务器有 Tomcat 6.0.14 和 Jetty 6.1.14,它们的实现方法各不相同。下面我们以 Tomcat 为例来说明开发Comet 风格的 Web 应用的步骤2。 本例以流方式实现了一个 Comet 应用。服务器每隔一定的时间间隔产生一个 0
9、9 之间的随机数,将数据推送到客户端。客户端接收并显示。 第一,要下载和安装 Tomcat6.X(本文截稿时,Tomcat 最新版本是6.0.24)。 第二,为了使用 Comet,要求服务器支持 NIO,所以要修改 Tomcat 配置文件 conf/server.xml, 即启用异步版本的 IO 连接器,这个非常关键。如下所示: 第三,该项目需要 Comet 的 API 支持,Tomcat6 自带的 Comet API 包为catalina.jar,在 Tomcat 安装目录下的 lib 目录中。 第四,编写 Servlet。通过 servlet 实现 CometProcessor 接口。这个
10、接口要求实现 event()方法,在配置的 Http11NioProtocol 调用 event()方法来处理请求,而不是 doGet 或 doPost。最基本的支持 Comet 的servlet 实现如程序清单 1 所示。 在 event()方法中,分别处理连接开始(BEGIN)、新数据可用(READ),连接结束(END),或出错等事件。Comet 允许针对不同的事件指定不同的连接超时。这意味着可以给常规的请求设置很短的生命周期,但是对于响应长连接请求的机制,可以将这个生命周期延长至几分钟。 TestComet Servlet 中,在连接开始时首先设置连接超时为 60 秒,接着启动一个推送数
11、据的线程。该线程的实现类为 RandomSender,如程序清单 2 所示。请注意,这个类含有一个 ServletResponse 对象。回头看看清单 1 中的 event()方法,当事件为 BEGIN 时,response 对象被传入到 RandomSender 中。RandomSender 的 run()使用 ServletResponse 将数据发送回客户机。因为要实现流风格的 Comet,所以不能关闭连接。而要使连接保持开启。如果要实现长轮询,则一旦发送完所有消息后,就要关闭连接。 第五,编写客户端。在客户端,发出 AJAX 请求。请求和常规请求差不多。程序清单 3 测试了最基本的 A
12、JAX 请求,它基于 XMLHttpRequest,能够很好地响应来自 Comet 服务器的事件。客户端在 readystate 为 3 时(数据仍在传输中)读取数据,从而无须关闭连接,就能读取处理服务器端返回的数据,将它显示在浏览器上。 第六,运行程序。首先进行部署,为了使程序正常运行,先要删除本应用的 lib 目录下的 catalina.jar,如果不这么做,会出现异常:java.lang.ClassCastException: org.apache.catalina.util.DefaultAnnotationProcessor cannot be cast to org.apache.
13、AnnotationProcessor。 最后,重启 Tomcat6,用 FireFox 浏览器调用主页面 index.jsp,就可以看到随机数不断地涌现。 3 结束语 采用 Comet 技术实现的 Java Web 实时系统带来了全新的交互性,目前 Java Web 服务器正在为实现 Comet 提供成熟、稳定的技术,不久的将来,Comet 将成为 Servlet 3.0 和 JavaEE6 的标准的一部分。 参考文献 1 GALPIN M. Developing with Comet and Java EB/OL.(2009-05-26)2009-08-18.http:/ 2 Apache Software Foundation. Advanced IO and Tomcat EB/OL. 2009-09-05.http:/tomcat.apache.org/tomcat-6.0-doc/aio.html.