服务器推送技术是最近在项目中有可能要用到的一项技术,所以提前研究了一下。
考虑这样一个场景,在各类WEB应用中,如果有其他用户更改了某位用户正在关注的任何消息,则用户希望得到通知。如果一个 Web 站点显示动态数据,如股价等,那么所有用户都必须立即得到关于变更的通知。
这些场景本身属于一类称为 “服务器推送” 的问题。通常,服务器是中心实体,服务器将首先获得关于所发生的任何更改的通知,服务器负责将此类更改通知所有连接的客户端。但遗憾的是,HTTP 是客户端-服务器通信的标准协议,它是无状态的,而且在某种意义上来说,也是一种单向的协议。HTTP 场景中的所有通信都必须由客户端发起,至服务器结束,然而我们所提到的场景的需求则完全相反。对于服务器推送来说,需要由服务器发起通信,并向客户端发送数据。HTTP 协议并无相关配置,Web 站点应用程序开发人员使用独创的方法来绕过这些问题,
目前来说,服务器推送(PUSH)机制大致分为几种:
1. 短轮询
客户端以固定(或可配置)的时间间隔与服务器联系,查找是否有新的更新可用。在大多数时候,这些轮询纯粹是浪费,因为服务器没有任何更新。这种方法不是没有代价的,它有两大主要问题:
首先,这种方法非常浪费网络资源,
其次,因为轮询有一定的时间间隔,因此客户端不会获得实时的更新。
2. 长轮询
长轮询是用于更新服务器数据的另外一种方法。这种方法的理念就是客户端建立连接,服务器阻塞连接(通过使请求线程在某些条件下处于等待状态),有数据可用时,服务器将通过阻塞的连接发送数据,随后关闭连接。客户端在接收到更新后,立即重新建立连接,服务器重复上述过程,以此实现近于实时的通信。然而,长轮询具有以下缺陷:
一般的浏览器默认允许每台服务器具有两个连接。在这种情况下,一个连接始终是繁忙状态。因而,UI 只有一个连接(也就是说,能力减半)可用于为用户请求提供服务。这可能会导致某些操作的性能降低。
仍然需要打开和关闭 HTTP 连接,如果采用的是非持久连接模式(keepAlive=false),那么这种方法的代价可能极高。
这种方法近于实时,但并非真正的实时。(当然,某些外部因素总是不可控的,比如网络延时,在任何方法中都会存在这些因素。)
3. 流传送
流通道(streaming channel)与长轮询大致相同,差别在于服务器不会关闭响应流。而是特意保持其处于打开状态,使浏览器认为还有更多数据即将到来。但是,流通道也有着自己的缺陷:
最大的问题就是数据刷新(flushing)。
如果发现套接字将打开较长的时间,某些浏览器实现可能会自行决定关闭套接字。在这种情况下,通道需要重新建立。
通常,第一个问题可通过为每个流响应附加垃圾有效载荷来解决,使响应数据足以填满缓冲区。第二个问题可通过 “保持活动” 或按固定间隔 “同步” 消息来欺瞒浏览器,使浏览器认为数据是以较慢的速率传入的。
下面介绍一些常用的实现推送的技术。
1. Comet
Comet 有时也称反向 Ajax 或服务器端推技术(server-side push)。其思想很简单:将数据直接从服务器推到浏览器,而不必等到浏览器请求数据。听起来简单,但是如果熟悉 Web 应用程序,尤其是 HTTP 协议,那么您就会知道,这绝不简单。实现 Comet 风格的 Web 应用程序,同时保证在浏览器和服务器上的可伸缩性,这只是在最近几年才成为可能。
对于 Apache Tomcat,若要使用 Comet,主要需要做两件事。首先,需要对 Tomcat 的配置文件 server.xml 稍作修改。默认情况下启用的是更典型的同步 IO 连接器。现在只需将它切换成异步版本,如下 所示。
<!-- This is the usual Connector, comment it out and add the NIO one --> <!-- Connector URIEncoding="utf-8" connectionTimeout="20000" port="8084" protocol="HTTP/1.1" redirectPort="8443"/ --> <Connector connectionTimeout="20000" port="8080" protocol="org.apache. coyote.http11.Http11NioProtocol" redirectPort="8443"/>
CometProcessor
接口要求实现event
方法。这是用于 Comet 交互的一个生命周期方法。Tomcat 将使用不同的CometEvent
实例调用。通过检查CometEvent
的eventType
,可以判断正处在生命周期的哪个阶段。当请求第一次传入时,即发生BEGIN
事件。READ
事件表明数据正在被发送,只有当请求为POST
时才需要该事件。遇到END
或ERROR
事件时,请求终止。
具体的例子,请参照这个地址。
而目前常用的Comet框架有:
CometD:是一个Dojo Fondation的项目,它提供了Bayeux protocol的javascript、java、perl、python及其他语言的实现。在CometD的站点里同时提供了Sun,IBM,BEA等公司的Comet的实现的产品的链接。
2. HTML5
HTML5提供了两种符合W3C标准的推送方式:SSE和Web Socket。
先介绍一下SSE(Server-sent-envets),以PHP服务端为例,
客户访问的页面是
sse.htm
<!DOCTYPE html> <html> <body> <script> var source = new EventSource('source.php'); source.onmessage = function(event){ var text = event.data; window.webkitNotifications.createNotification('', 'Alert', text).show(); } </script> </body> </html>
source.php
header("Content-Type: text/event-stream"); header("Cache-Control: no-cache"); mysql_connect("localhost", "user", "pass"); mysql_select_db("eventstream"); $q = mysql_query("select textnotif from notification where read='0'"); $r = mysql_fetch_array($q); $notif = $r[textnotif]; if($notif != ""){ echo "data: ".$notif.PHP_EOL; }
而WEB Socket提供了一个双向的消息通道。WebSocket 是通过 HTTP 协议的初始握手阶段然后升级到 Web Socket 协议以支持实时数据通信。WebSocket 协议设计了更为轻量级的 Header。
这里可以参考到一个使用WebSocket技术的实例。而目前网上关于WebSocket的内容还是比较多的。
由于WebSocket具有双向沟通的优势,因此可以实现聊天室、游戏、股票交易等等需要双向通讯的应用上。而SSE虽然只能实现从服务器向客户端的单向推送,但是它可以自动重新链接,具备EventID等优势,因此也有用武之地。
而另外一个可作参考的轻量级的服务器推送框架是Pushlets,它提供了从HTTP Push到DHTML,以及Pushlets框架的具体实现实例。
参考: