Web 服务端推送技术介绍
Web 服务端推送技术,作为客户端与服务器端实时交互的解决方案,经常用于一些实时性要求高的 Web 应用系统中。目前,经常使用的两项服务端推送技术有 Comet 和 Websocket。
Comet 技术模型
在 Websocket 出现之前,Comet 是最主要的服务端推送技术。众所周知,超文本传输协议是一种请求/响应协议。HTTP 定义了三个对象,分别是客户端,代理和服务端。客户端通过和服务端建立连接来发送 HTTP 请求。服务端接受来自客户端的连接,然后通过发送响应信息来服务 HTTP 请求。代理是中间实体,它可以用来转发传递客户端到服务端的请求,同样,也可以用来转发传递服务端到客户端的响应。
在标准的 HTTP 模型中,服务端不能初始化到客户端的连接,也不能发送一个未被客户端请求的响应;因而服务端不能发送异步事件到客户端。为了尽可能的接受异步事件,客户端需要周期性的去服务端拉取新的内容。然而,当服务端没有可用的新内容的时候,连续请求会消耗很多带宽资源。持续请求的效率也很低,因为它降低了应用的响应能力,在服务端接受到下一次客户端轮询请求之前,数据一直在排队。为了消除传统 Web 模型的局限以及依靠轮训来提高实时性的弊端,多种服务端推送编程机制被实现,这些机制被统称为“Comet”模型。
在 Web 开发中使用 Comet 技术的时间要早于 Comet 这个词来描述这些技术。根据技术方案的不同,Comet 又被称为 Ajax 推送、反向 Ajax、双向 Web、Http 流、Http 服务器推送等。Comet 的实现方式基本可以划分为两类:流和长轮询。
基于流的 Comet
基于流的 Comet 是指打开一个单独的持久链接用于浏览器和服务器之间的所有 Comet 事件。服务器发送事件,客户端对事件进行处理,而 HTTP 链接不会关闭。基于流的 Comet 包含使用隐藏帧和 XHR 两种基础技术的实现。
使用隐藏帧的好处是简单而且通用性、跨浏览器支持非常好,只要支持<iframe>标签即可。但是缺点也很明显,一是没有可靠的错误处理方法,二是无法跟踪请求调用过程的状态。
XHR 对象是 Ajax 应用中浏览器和服务器通信的主要工具。通过为 XHR 生成一个定制的数据格式并有 JavaScript 负责完成解析,它也可以用于 Comet 消息推送。此方案依赖于浏览器接收到新数据是触发的 onreadystatechange 回调。这种方式易于跟踪请求处理,但是跨浏览器的支持不如隐藏帧,因为浏览器厂商对于 XHR 的支持并不完全一致,如 IE 使用的是 ActiveX 对象,而在 Firefox 中则是一个内置的 Javascript 类。此外,由于浏览器的安全策略,为了避免跨站脚本攻击,XHR 存在跨域访问的限制。
基于长轮询
基于流的方案,对于现在浏览器来说,都存在不可避免的负作用,迫使业界又实现了几种复杂的流传输,并根据浏览器进行切换。由此,许多 Comet 采用了长轮询的方式,这种方式更易于在浏览器端实现。区别于普通 ajax 轮询,长轮询需要客户端通过 Ajax 发送请求,在服务端获取数据,如果没有数据则线程等待有数据过来唤醒线程并返回数据,处理完毕后关闭链接。然后浏览器再发起新的长轮询请求以处理后续的事件。
基于长轮询的 Comet 包含 XHR 长轮询和 Script 标签长轮询两种实现方式。前者同样存在跨域的问题,如果没有启用跨域资源共享,此时,Comet 事件是不能用于修改主页面的 HTML 的,这种问题可以通过设置代理服务器的方式来规避,这使得他们看起来好像源于一个域,但是这增加了部署的复杂性,并降低了访问性能,因此并不是一个好的方式。而后者在 HTML 中<script>标签可以只想任何 URI,并且响应中 Javascript 代码可以在当前 HTML 文档中执行。因此一个基于长轮询的 Comet 传输可以通过动态创建<script>标签并将其源指向 Comet 服务器来实现。当浏览器加载新增的<script>标签时,服务端以 JavaScript 形式返回 Comet 事件,当<script>请求完成时,浏览器会再创建新的<script>标签以接收新的事件。后者的优势是跨浏览器支持,而不存在跨域的问题,但会存在潜在的风险尽管这种风险可以通过 JSONP 避免。
WebSocket 技术
Comet 是在 HTTP 单向通信的基础上模拟服务器与客户端浏览器的双向通信,不同的 Comet 方案存在不同的缺陷,无论是跨浏览器还是规范限制,而且因为是一种模拟实现,它的效率并不高(如 HTTP 报销头的开销,尤其是发送消息较小的情况)。
基于这些原因,人们试图从规范角度寻找一种标准的替代方案。HTML5 提供了一种全新的协议来解决这个问题,这就是 WebSocket,一种实现了客户端与服务器之间的全双工通信的协议,他可以更好的节省服务器资源以及带宽以提供实时通信协议。
Websocket 是独立的基于 TCP 的协议,建立 WebSocket 链接时,客户端首先发送一个握手请求,服务器返回一个握手响应,握手为 HTTP Upgrade 请求,因此服务器可以通过 HTTP 端口进行处理,并将通信切换至 WebSocket 协议。握手成功之后,客户端与服务器之间就可以基于 WebSocket 协议进行全双工通信了。
WebSocket 与 HTTP 协议完全不同,他们之间的关系仅限于 WebSocket 通过 HTTP 协议的 Upgrade 请求完成的。WebSocket 之所以如此设计,旨在不损害网络安全的前提下解决全双工通信的问题。目前主流的浏览器均已支持 WebSocket。在服务器方面主要的几款开源 Servlet 容器(Tomcat、jetty、Undertow)都支持 WebSocket。
WebSocket 协议定义了 ws://和 wss://两个前缀分别来表示非加密和加密链接。除去协议前缀外,其链接的具体语法格式与 HTTP 相同。
其中 Upgrade: websocket 表明这是一个 WebSocket 请求,Sec-WebSocket-Key 是客户端发送的 base64 编码的密文,要求服务端必须返回一个对应加密的 Sec-WebSocket-Accept 头信息作为应答。
HTTP 101 状态码表明服务端已经识别并切换为 WebSocket 协议,Sec-WebSocket-Accept 是服务端采用与客户端一直的密钥计算出来的信息。
如果在与 Apache 或者 Nginx 集成的情况下使用 WebSocket,通常需要进行额外的配置,具体可参见:
Apache:mod_proxy_wstunnel - Apache HTTP Server Version 2.4
Nginx: http://www.ngnix.com.blog/websocket-nginx/
既然 WebSocket 是 HTML5 新增的特性,那么在使用时就要考虑浏览器旧版本兼容的问题,这也是 Comet 方案尽管存在各种问题但是仍旧被采用的原因。
总结
本文简单介绍 Web 服务端推送技术的相关内容,包括 Comet 和 WebSocket,使用这两项技术就可以开发有实时性要求的 Web 应用系统。一个健壮的服务器推送方案非常复杂,远不是过几千字的文章就可以说清楚的,感兴趣的读者可以进一步阅读相关资料。
更多学习资料点击下方
评论