WebSockets与Bayeux/CometD

此时有两种技术可以将通信引入基于浏览器的应用之中:Bayeux(又称CometD)和WebSockets。究竟接下来会是一方被另一方所取代,还是双方藉由显著的差异化实现共同发展呢?

CometD框架是Bayeux协议的实现,可以使得服务器端和客户端在不可靠的网络上进行多通道异步通信。该实现用到了多种语言(JavaScript、Java、Perl等等),但是主要还是基于浏览器的AJAX应用。Bayeux的优势在于,它可以运行在任何支持AJAX的浏览器上,在现有HTTP通信机制下,就能够让浏览器支持异步后台的更新,比如类似于Google邮件的新邮件通知的新信息送达。事实上,同样的协议还可以用于使用其他语言在不可靠的网络上连接设备进行通信(比如移动设备)。

WebSockets是一个标准草案,这项草案由Google、Apple和其他进行HTML 5标准化的WhatWG工作组成员所资助。因此,支持HTML 5的浏览器(Chrome或者Safari)已经开始支持内建的WebSocket协议。

这两个协议的目标都是让基于Web的AJAX应用能通过异步消息或者基于Socket的连接进行通信,而不是在一个现有应用之上再搭建一个自己实现的通信层。这使得在设计应用的时候,可以只关注于组件部分,而把消息传递给通信层去递送。另外,这两个协议都能够建立长连接,事件可以通过长连接异步地递送给应用。这没什么新鲜的:HTTP 1.1就支持连接管道(可以在每个请求之后保持连接,并可以在第一个请求得到处理之后再发送多个请求);而像IMAP等一些协议则支持IDLE命令,把连接置成休眠状态,这样在休眠的连接上就不再有数据传输,但是服务器依然可以随时推送新消息。其实在Bayeux或者WebSockets之前,通过HTTP进行持久通信的机制一般被称为“HTTP推送”。

然而,长连接并不是没有任何问题。一条连接如果长时间没有数据通信的话,会被认为已经死掉,并在接下来的某个时间点被终止。为了解决这个问题,IMAP的IDLE建议客户端每29分钟发送一个IDLE命令来避免断连。而HTTP代理则会决定连接是处于闲置状态并丢弃连接,而不去管客户端和服务器端是否已经保持了一条长连接。

资源限制也是一个问题。通常,浏览器都会限制对单个服务器的并发HTTP连接的数量,以避免对该服务器(或者网络连接)造成过大的压力。浏览器一般会将这个并发连接数量限制在每次2到4个。

Bayeux和WebSockets都试图避免资源限制问题,使用回退机制来实现长轮询(比如Bayeux),或者切换到其他非HTTP协议之上。那么,这些程序库的使用者就不需要再担心浏览器或者基础架构的限制问题。

Erlang之父Joe Armstrong认为,WebSockets将会干掉Comet:

在经过了一些试验以后,我可以让Erlang和一张Web网页通过纯异步消息传输进行通信。

我认为这意味着如下的技术将会消亡:

  • comet
  • 长轮询
  • AJAX
  • 连接保持socket

其实核心的问题便是,Web浏览器不能像其他应用一样,打开一个socket并进行异步I/O。而上述技术只不过是些带有各种问题的权宜之计而已。

Jetty和Bayeux协议的创作者之一Greg Wilkins认为,WebSocket规范还不够好,无论是规范的定义,还是连接中断时的行为,都需要改进和完善。其中一个关键问题,便出在协议的表述上:

然而这类规范的一个更实际问题便是规范写的很让人费解,比如:

使得/b_v/为整数,并且为/b/的低7位(这个值应由/b/和0x7F进行_and_位运算求得)。

将/length/乘以128,再加上/b_v/,并将结果储存在/length/中。

如果/b/的高序位被置(例如,/b/和0x80进行_and_位运算得到0x80),那么返回到标记为_length_的前一步骤。

我质疑到读者能否确认客户端的数据帧和服务器端的数据帧是对称的,并且由相同的数据帧实现。

相比较这些诘屈聱牙的论述,IETF规范通常使用精确的扩展巴科斯范式(ABNF,RFC5234)来描述协议,这样便不会引起误读或者混乱。为了能更清楚的说明,我将4.2节翻译成了BNF …

值得注意的是,Jetty现在既支持WebSocket又支持Bayeux。本着调查研究的精神,Greg写了一篇博客,通过WebSocket实现了聊天功能。然而开发过程却并不顺利:

聊天室的典型用例是这样的,你进入了一个聊天室,标识出你的存在,这个标识会一直保持到你显式地退出聊天室。而在Web聊天的情况下,你可以收发聊天消息,直到你关闭浏览器或者转至其他网页。不幸的是,即便是这么简单的用例都无法通过WebSocket实现,因为在该协议中,连接有一个闲置超时。

为了保持存在的状态,聊天应用要发送“连接保持(Keep Alive)”的消息给WebSocket,来避免该连接因为闲置超时而关闭。然而,应用并不知道这个闲置超时究竟是多少,因此它只能随便选一个间隔周期(比如30秒)来发送该消息,这和长轮询要做的事似乎就多少有些类似了。

通过onClose处理、连接保持、消息队列、超时和重试,我们最终实现了一个可以在用户停留在网页上的时候保持其存在状态的聊天室。但是遗憾的是这个聊天室依然还没有完,因为它还需要处理错误和非暂时性故障。

Greg对WebSocket协议的改进给出了一系列的建议,这些建议力图在将来给开发者们带来更多的便捷:

  • WebSocket将来的版本最好能支持超时发现机制,这样它就可以告诉应用连接保持消息的周期,甚至它能够发连接保持的消息给应用。
  • WebSocket将来的版本最好能支持有序关闭消息,这样应用就可以分辨出关闭的原因是网络错误(需要将用户的存在状态保持一段时间)还是一次有序关闭,就比如说用户离开了该页面(将用户的存在状态移除)。
  • WebSocket将来的版本最好能支持顺序关闭,这样没有出错的连接上的递送便可以识别出来,除非服务质量的要求非常高,否则便可以避免复杂的确认。
  • WebSocket将来的版本最好能支持更详细的连接错误,处理“找不到主机名”和处理“401未授权”的方式肯定是不一样的。
  • WebSocket将来的版本最好能支持发送错误状态,比如网络错误或者是闲置超时,这样应用就可以不必对错误进行重试。

最后,他概要性地总结了自己的观点,并谈了谈对未来的期望:

这篇博客说明了WebSocket不能解决很多在开发健壮Comet网络应用时遇到的复杂问题,也就是说没有银弹。希望在未来版本的WebSocket中,可以加入连接保持、超时处理、顺序关闭和错误通知等特性。但是WebSocket却不能提供高级的队列处理、超时、重连、重试和退避(Backoff)。如果你想拥有高品质的服务,那么无论是你的应用还是你的框架,都需要支持这些特性才行。

CometD版本2即将发布,支持将WebSocket作为替代传输层,来支持现有的JSON长轮询和JSONP回调轮询。CometD支持这篇博客所谈到的所有特性,并让它们对浏览器完全透明,无论是否支持WebSocket都可以做到。我们真心希望WebSocket可以给我们更大的吞吐量和更低的延迟,比现在已经让人印象深刻的长轮询要更好。

Jetty 8.0.0 M0已经发布,提供了对WebSocket的支持,同时还支持Servlet 3.0 API。

查看英文原文:WebSockets and Bayeux/CometD

你可能感兴趣的:(WebSockets与Bayeux/CometD)