Ajax、Comet、HTML 5 Web Sockets 技术比较分析

  • Dio Synodinos
  • 沙晓兰

2008 年 12 月 24 日

话题:Java.NETRuby语言 & 开发架构

九十年代中期,WWW 以迅猛之势转眼跻身传播信息的主要渠道之一。浏览器的身影开始无处不在,用户也随之开始适应这种信息传播方式。显然,WWW 提 供的应用平台能够赢得历史上任何一个平台都无法比及的用户量。但当时很难实现这样的目标是因为一些标准(HTML、HTTP 等)都不很完善,这些标准设计 的时候都没有考虑到高度交互和富客户体验。最初的一些富在线应用基本上都是由 Microsoft Exchange 开发组实现的。96 年以来,他们曾采用 IFrame 为邮件服务器系统提供 Outlook 类型的前端应用。这些早期尝试在响应能力和整体的 用户体验方面都非常落后,但从这些应用身上却可以清楚地看到即将兴起的网络应用。1998 年,团队开始为 MS Exchange Server 2000 编写 web 前端,他们开发了 XMLHTTP,这个控件实现了单个 web 页面与服务器间的异步交互。可以看到,XMLHTTP 实际上根本没有立即和 XML 捆绑起来。XMLHTTP 这个名字是 Alex Hopmann 提出的,他是后来加入开发团队的,据说名字采用这个前缀的唯一的原因是 IE5 当时正在准备第二个 beta 版本,而这个控件必须作为这个版本 的 MSXML 库的一部分发布,这才冠上了 XML。 

Mozilla 基金会在 2002 年开发他们的浏览器的一个版本时,也以 XMLHttpRequest 的形式实现这一新技术,这个浏览器就是后来的 Firefox。尽管当时有一些商家也曾尝试运用这些新 API,但他们采用的的这种远程脚本程序的模式一直没有引起公众的注意,直到 Google 开始部署 基于 JavaScript 和 XHR 的一系列新型服务。当时的第一个服务是 2005 年 2 月 8 日 Google Blog 上发布的 Google Maps。之后不久,XHR 就一跃成为业界最炙手可热的话题。直到那时,也还没人预料到 XHR 给 Web 应用开发带来的革命性的推动,但它的成功开始让我们 转变之前对 WWW 的一些看法。 

Kaazing Gateway发布之际,InfoQ 采访了 Richard Smith,谈到关于AJAX, Comet以及蒸蒸日上的HTML 5 Web Sockets等技术的发展情况:

Ajax 为 HTTP 通信模型提供了很好的解决方案,它在客户端异步轮询服务器端事件。服务器事件依次排列在待处理队列中,根据轮询时间隙依次传送到 浏览器,这样模拟服务器发起的通信,在轮询时间隙间进行实时消息传递。因此,仅仅依靠 Ajax, 我们永远都不可能实现真正的实时通信。 

Comet 引入的优化针对的是 HTTP 通信初始之时,它在 HTTP 基础上采用“push”通信风格。Comet 提供的几项技术能够在没有客户端发送 请求的前提下让服务器主动将信息发送到浏览器。如果再增加一个额外的 HTTP 连接的话,Comet 甚至可以在两个 HTTP 连接上实现双向通信。但 Comet 的绊脚石在于各个浏览器提供商对 XHR、iFrames——这两种实现 Comet 所需的数据块的支持程度不尽相同,没有统一的实现标准。另外, 无论是从网络还是开发角度来看,Comet 管理两个连接的开销都很大。这些开销带来的直接影响就是 Comet 应用中的传输延时,限制了它们所提供的实时通 信的精确性。

HTML 5 WebSocket 代表的是 Comet 和 Ajax 推进 HTTP 通信新一轮的尝试。HTML 5 WebSocket 规范中定义,在浏览器和服务器之间采用单 socket 全双工(或者叫双向)传输来 push 和 pull 信息。这不但可以避免 Comet 中 存在的连接和可移植问题,还能够提供比 Ajax 轮询更高效的解决方案。目前,HTML 5 WebSocket 是推动 web 全双工实时通信的主要机制。

Richard 认为,AJAX 和 Comet 这两种方式都有各自的局限:

要通过 Ajax 来模拟服务器端起始的通信就需要轮询机制,而这一机制不顾应用的状态改变盲目检查更新,结果就是 CPU 周期和内存毫无必要地过早或者 太晚侦测服务器的更新,客户和服务器两端的资源利用状况因此都相当差劲。所以,传统的 Ajax 应用必须根据服务器上事件的发布率不停地调整轮询时间隙的长 短,才能改善各个请求的准确度。另外,高频率轮询会加重网络承载,拖累服务器;低频率轮询又会错过更新和传送一些失去时效的信息。无论哪种情况,消息传递过程都无法避免传输延时。短间隙轮询开销很大,因为要支持这样的小型服务器 ping 需要大量服务器资源。 

Comet 维护服务器和浏览器之间的持续连接和长时间有效的 HTTP 请求,以此来尝试“push”型通信。这种连接下,服务器可以发送事件,但连接是由客户端浏览器发起。逆流请 求也可以看做是浏览器向服务器发送的请求,这需要一个额外的 HTTP 连接。Comet 因此可以利用跨两个 HTTP 连接的双向通信。但是,维护这两个连接会 消耗服务器端大量资源,因为这也意味着服务一个顾客需要消耗双倍资源。而且,浏览器的配置往往都是通过域来限制 HTTP 连接。Comet 的应用因此更为复 杂,还常常会要求运用一些像多路复用那样复杂的技术,再或者就是要管理多个域。 

有一些 Comet 解决方案试图降低长轮询技术导致的资源消耗,但这一技术发送太多的 HTTP 请求 / 回复头信息。比如,服务器发送的 每个事件,都通过客户浏览器为连接提供服务,这迫使浏览器必须和服务器重新建立连接。这一动作又引发了另一个客户端请求以及服务器在长轮询时间隙中发送回复。很多时候,回复中的 HTTP 头内容完全超过了传送的信息。

From client (browser) to server:
GET /long-polling HTTP/1.1\r\n
Host: www.kaazing.com\r\n
User-Agent: Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9) Gecko/2008061017 Firefox/3.0\r\n
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\n
Accept-Language: en-us,en;q=0.5\r\n
Accept-Encoding: gzip,deflate\r\n
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7\r\n
Keep-Alive: 300\r\n
Connection: keep-alive\r\n
Cache-Control: max-age=0\r\n
\r\n


From server to client (browser):
Date: Tue, 16 Aug 2008 00:00:00 GMT\r\n
Server: Apache/2.2.9 (Unix)\r\n
Content-Type: text/plain\r\n
Content-Length: 12\r\n
\r\n
Hello, world

用 Comet 开发有很多挑战。实现你自己的解决方案不是不可能,但这需要开发 JavaScript 类库,借用 frame 和 XHR Streaming 等很多技术来维护持续连接。这时候,问题就在于不同的浏览器对这些技术有不同的实现,更糟的是,它们往往都依赖服务器端代码推送 JavaScript 代码段,不仅增加了整个实现的复杂程度,还引入了移植性的问题。 

还好,现在有几个框架提供了这些传输的抽象,简化了 Comet 开发。其中最著名的就是 SitePen 的 Cometd,其实现完全参 考 Bayuex 规范。Bayuex 规范定义了 Comet 的 publish-subscribe 模型。Jetty 近期版本也包含了基于 Java 的服务器端的 Bayeux 实现。 

Bayuex 和 Cometd 着实简化了 Comet,然而它的 API 和 wire 协议还是有很多争议。Comet Daily 上有一个 “Coliding Comet: Battle of the Bayuex”系列就专门深入讨论关于 Bayuex 的各种问题。

Richard 还认为 HTML 5 WebSockets 可以从当前的各种解决方案中获得很多帮助:

尽管 Comet 和 Ajax 都可以实现提供桌面应用功能的终端用户体验,而且传输延时也可以缩短到用户无法感知的程度,但仍然只有 Web Sockets 才能真正为浏览器提供精确、高效的流事件,保证传输延时可以微乎其微,直至忽略不计。这是目前为止通过 web 发送实时信息最出色的解决方 案。它不仅通过单个 TCP/IP 连接提供完整的异步双工道流通信,而且新的 HTTP 头的应用也非常有利,更重要的是它能够支持浏览器和源服务的消息采用同 样的格式。 

多数 Comet 实现都依赖 Bayeux 协议。该协议要求源服务发出的消息必须转换成 Bayeux 协议支持的格式,这一并不必要的转 换反而使得整个系统更加复杂,开发员不得不在服务器端处理一种消息格式(比如 JMS、IMAP、XMPP 等),在客户端又要处理另一种消息格式(比如 Bayeux、JSON)。而且实现将源协议转换到 Bayeux 的代码硬是要在发送消息之前对消息本身进行解析和处理,这又给系统增添了不必要的性能负 载。采用 Web Sockets 的话,就不会有因为转换代码而增加系统的复杂性,也就不用为这方面的性能担忧。 

WebSockets 经常遇到的一个问题是它是否可行。目前来看,浏览器本身没法直接支持这项技术。但再过几个月就肯定可以了,像 WebKit、Firefox 和 Opera 这样的浏览器从来就对 HTML 5 的特性——比如 Canvas、postMessage、离线存储和服务器端发送信息 (SSE) 等反应迅速,及时添加相应的支持。 

WebSockets 还需要服务端一定程度的支持,因为现存 HTTP 连接更新到新连接需要 HTTP 的一个起始“握手”。 Kaazing Gateway 开源项目实现了第一个支持这一动作的服务器,并且拥有能够支持成千上万持续连接所需的扩展性。 Kaazing Gateway 的供应商 Kaazing 还提供了一个可以让当前所有 web 浏览器都支持 WebSockets 的 JavaScript 类库。所以,目前对 WebSocket 的支持也可说是准备就绪了。 

为了支持 HTML 5 WebSockets,Kaazing 发布了Kaazing Gateway 8.09_2 Atlantis,这是一个开源 HTML 5 WebSocket 服务器,可以在 Mozilla 公共许可的衍生许可—— OSI approved Common Public Attribution License (CPAL)下使用:

Kaazing Gateway 提供 JavaScript 类库来模拟 HTML 5 WebSocket,开发员现在就可以开始运用 WebSockets,结合 WebSocket 接口创建的应用在当前甚或是未来的浏览器上都可以部署。 

Kaazing Gateway 背后的超高性能服务器的单个节点能够支持成千上万的并发连接。多实例通过传统的 HTTP 负载平衡或者 DNS round robin 算法集群分类,因此能够支持无数个持续客户连接。除了大量的连接之外,Kaazing Gateway 的高性能和分级事件驱动构架(SEDA)还推动它本身能够处理高数据吞吐量。 

Kaazing Gateway 的 Atlantis 发布还为流行的消息服务(诸如 Apache ActiveMQ、RabbittMQ)和 XMPP 服务(诸如 OpenFire、Jabberd 和其它一些流行的聊天服务器)打包了 JavaScript 客户端。这样,创建那些基于 web 的聊天应用或是 stock matrixes、网上交易平台、在线游戏等消息发送应用就更为简单了。

计划中的 Kaazing Gateway 8.12 发布把目标瞄准了更多的 HTML 5 特性,例如服务器端发送事件(Server-sent Event)、更先进的安全服务,以及对XMPPJabber)、STOMP (比如ActiveMQ、RabbitMQOpenMQ) 等的扩展支持:

它所提供的类库能够让目前的浏览器都支持 HTML 5 服务器发送事件,引进了 HTML 5 postMessage 的支持,无疑方便了跨文档的消息传递。Kaazing 的 HTML 5 类库还包括对 HTML 5 离线存储的支持,提供简易、基于 DOM 的存储解决方案。Kaazing Gateway 及其客户类库现在还为跨站请求支持 W3C 访问控制,这一机制能够让客户端启动跨站请求,比较多的提法是跨站 XmlHttpRequests。 

除了对 HTML 5 的扩展支持以外,Kaazing Gateway 8.12 还提供更高级的 XMPP 特性,比如群聊。这一发布版本还引进了 STOMP-JMS 适配器,因此,结合 Kaazing Gateway 还能适配任何现存的 Java 消息服务(JMS)(例如 JBoss Messaging、Tibco EMS、OpenMQ、SwiftMQ、WebSphere MQ 等等)。

您可以在 InfoQ 上找到更多关于 AJAXComet富网络应用的信息。

查看英文原文:HTML 5 Web Sockets vs. Comet and Ajax

Java.NETRuby语言 & 开发架构