HTML5 Web Sockets 与代理服务器交互

阅读数:15729 2010 年 5 月 5 日

随着最近 WebSocket 服务器实现的高速增长,对于 HTML5 Web Socket 如何处理代理服务器,防火墙,以及负载平衡路由器出现了许多疑问。代理服务器是否会自动中断 WebSocket 连接?HTML5 Web Sockets 是否能比 Comet 更好的处理防火墙与代理服务器问题呢?Web Sockets 又是否是实行无缝的代理服务器遍历的银弹呢?在这篇文章中,我将会解释 HTML5 Web Sockets 是如何与代理服务器,负载平衡路由器以及防火墙进行交互的。此外,我将会解释 Kazzing WebSocket 网关及其 Web Socket 模仿功能如何带来额外的价值。

关于 HTML5 Web Sockets 与代理服务器

让我们从一些基本概念开始:HTML5 Web Sockets 和代理服务器究竟是什么?

HTML5 Web Sockets

HTML5 Web Sockets 规范定义了Web Sockets API,支持页面使用 Web Socket 协议与远程主机进行全双工的通信。它引入了 WebSocket 接口并且定义了一个全双工的通信通道,通过一个单一的套接字在 Web 上进行操作。HTML5 Web Sockets 以最小的开销高效地提供了 Web 连接。相较于经常需要使用推送实时数据到客户端甚至通过维护两个 HTTP 连接来模拟全双工连接的旧的轮询或长轮询(Comet)来说,这就极大的减少了不必要的网络流量与延迟。

要使用 HTML5 Web Sockets 从一个 Web 客户端连接到一个远程端点,你要创建一个新的 WebSocket 实例并为之提供一个 URL 来表示你想要连接到的远程端点。该规范定义了ws://以及wss://模式来分别表示 WebSocket 和安全 WebSocket 连接。一个 WebSocket 连接是在客户端与服务器之间 HTTP 协议的初始握手阶段将其升级到 Web Socket 协议来建立的,其底层仍是 TCP/IP 连接。

Proxy Servers

一个代理服务器是作为客户端与另一个服务器之间的一个中介(比如,因特网上的一个 web 服务器)。代理服务器通常被用作内容缓存,因特网连接,安全,以及企业内容过滤。典型的,一个代理服务器被架设在私有网络与因特网之间。代理服务器可以监控流量并断开连接,如果该连接已打开很久。对于使用长期活跃连接的 web 应用来说(比如,Comet HTTP 流或者 HTML5 Web Sockets),代理服务器的问题是明显的:HTTP 代理服务器——原本最初设计用来文档转移——可能会选择关闭流或闲置的 WebSocket 连接,因为它们看起好像是尝试连接一个没有回应的 HTTP 服务器。这一行为对于长久的连接,比如 WebSockets 来说,是存在问题的。另外,代理服务器可能会缓冲未加密的 HTTP 响应,这将会对 HTTP 响应流带来不可估计的延迟。

HTML5 Web Sockets 和代理服务器

让我们看看 HTML5 Web Sockets 是如何与代理服务器工作的。WebSocket 连接使用标准 HTTP 端口(80 和 443),这让很多人管它叫做“代理服务器和防火墙友好协议”。因此,HTML5 Web Sockets 不需要安装新的硬件,或者要求公司网络开放新的端口——这两件事足以使任何新的协议夭折。在浏览器与 WebSocket 服务器之间不需要任何中间服务器(代理或反向代理服务器,防火墙,负载平衡路由器等等),只要服务器和客户端双方都理解 Web Socket 协议,WebSocket 连接就可以顺利的建立。然而,在真实的环境中,许多网络流量都被路由到中间服务器。

一图胜千言。图 1 展示了一个简化的网络拓扑,客户端使用浏览器来访问后端基于 TCP 的服务,这里使用了全双工的 HTML5 WebSocket 连接。一些客户端位于公司网络内部,受到公司防火墙的保护,并被配置为通过显式的或者已知的代理服务器来访问因特网,这些代理服务器会提供内容缓存和安全;而其它的客户端则直接在因特网上访问 WebSocket。两种情况中,客户端的请求都可能被路由到透明的,或者未知的代理服务器(例如,一个数据中心的代理服务器或者远程服务器前端的一个反向代理服务器)。代理服务器甚至还有可能有它们自己的显式的代理服务器,这又增加了 WebSocket 传输需要的中继数。

图 1——有着显式和透明的代理服务器的 Web Sockets 架构

跟常规的使用请求 / 响应协议的 HTTP 传输不一样,WebSocket 连接可以长时间保持打开。代理服务器也许可以支持这点并优雅地处理,但它们同时也可能带来破坏因素。

WebSocket Upgrade

HTML5 Web Sockets 使用 HTTP Upgrade 机制升级到 Web Socket 协议。HTML5 Web Sockets 有着兼容 HTTP 的握手机制,因此 HTTP 服务器可以与 WebSocket 服务器共享默认的 HTTP 与 HTTPS 端(80 和 443)。要建立一个 WebSocket 连接,客户端和服务器在初次握手的时候从 HTTP 协议提升到 Web Socket 协议,如例 1 所展示的。一旦连接建立,WebSocket 数据帧就可以以全双工的模式在客户端和服务器之间来回传输。

例 1—WebSocket 升级握手

客户端到服务器:

GET /demo HTTP/1.1
Upgrade: WebSocket
Connection: Upgrade
Host: example.com
Origin: http://example.com
WebSocket-Protocol: sample 

服务器到客户端:

HTTP/1.1 101 Web Socket Protocol Handshake
Upgrade: WebSocket
Connection: Upgrade
WebSocket-Origin: http://example.com
WebSocket-Location: ws://example.com/demo
WebSocket-Protocol: sample 

未加密的 WebSocket 连接

Web Socket 协议本身并不知道代理服务器与防火墙;它只定义了 WebSocket 升级握手与 WebSocket 数据帧的格式。让我们看看在两种代理服务器场景(显式的和透明的)中未加密 WebSocket 传输的情况。

未加密 WebSocket 连接与显式代理服务器

如果一个浏览器被配置为使用显式的代理服务器,那么在建立 WebSocket 连接时它首先向代理服务器发出 HTTP CONNECT方法。比如,要使用ws://模式(通常是通过 80 端口)连接到服务器 example.com,浏览器客户端就会像例 2 所示的那样向代理服务器发出 HTTP CONNECT方法。

例 2—使用 80 端口的 HTTP CONNECT方法

CONNECT example.com:80 HTTP/1.1
Host: example.com 

当这个显式的代理服务器允许CONNECT方法时,WebSocket 连接升级握手得以完成,握手成功后,WebSocket 信息流就可以畅通无阻地通过代理服务器传输了。

未加密的 WebSocket 连接与透明代理服务器

如果是未加密的 WebSocket 信息流通过透明的代理传输到 WebSocket 服务器这种情况,实践中连接很可能失败,因为在这种情况下,浏览器没有发出CONNECT方法。当代理服务器将请求转发到(WebSocket)服务器时,将会被剥去一些特定的消息头,包括 Connection 消息头。因此,一个工作良好的透明代理服务器几乎会立刻引起 WebSocekt 升级握手失败。

并非所有的代理服务器在所期待的代理行为方面都遵循 HTTP 标准。比如,一些代理服务器被配置为不删除Connection: Upgrade消息头,并将其传送给 WebSocket 服务器,反过来服务器又会发送 101 Web Socket Protocol Handshake 响应。而当客户端或服务器开始发送第一个 WebSocket 帧时问题就出现了。因为这帧数据与代理服务器所期待的任何事物(比如常规的 HTTP 流量)都不相同,就可能会产生一些异常,除非代理服务器被特别配置为处理 WebSocket 流量。

逐跳升级

在 WebSocket 的握手阶段,Connection: Upgrade消息头被发送给 WebSocket 服务器。如果代理服务器不得不参与进升级机制的话,额外的代理服务器就必不可少,因为使用了逐跳传送;从代理服务器发送到浏览器的升级只适用于一跳,而代理服务器必须发送它自己的升级消息头来处理从代理服务器到 WebSocket 服务器(或另一个中间服务器)的下一跳。同时代理服务器还必须停止按 HTTP 来处理该请求。

目前,大部分透明代理服务器都还不了解 Web Socket 协议,而这些代理服务器也无法支持 Web Socket 协议。然而,未来,代理服务器将会是 Web Socket 可知的,并且能够合理的处理与转发 WebSocket 信息流。

加密的 WebSocket 连接

现在,让我们来看看加密的 WebSocket 信息流在两种不同的代理服务器场景下的情况(显式的和透明的)。

加密的 WebSocket 连接与显式的代理服务器

如果一个浏览器被配置为使用显式的代理服务器,那么在建立 WebSocket 连接时它首先会向该代理服务器发出一个 HTTP CONNECT方法。比如,要使用wss://模式(典型地是通过 443 端口),连接到服务器 example.com,浏览器客户端会发送如例 3 所示的 HTTP CONNECT方法到代理服务器。

例 3——使用端口 443 的 HTTP CONNECT方法

CONNECT example.com:443 HTTP/1.1
Host: example.com 

当显式的代理服务器允许CONNECT方法时,TLS 握手被发送出去,后面紧跟着 WebSocket 连接升级握手。这系列的握手成功后,WebSocket 信息流就可以开始无阻碍的通过代理服务器了。HTTPS(HTTP over TLS)也是以同样的方式工作的;使用加密通常会触发 HTTP CONNECT方法。

加密的 WebSocket 连接与透明的代理服务器

如果是透明代理服务器的情况,浏览器不知道代理服务器,因此不会有 HTTP CONNECT方法被发送。然而,因为线上信息是加密的,中间的透明代理服务器可能会简单的让加密的信息通过。因此,如果使用了 Web Sockets 安全的话,WebSocket 连接成功的几率要大得多。因此,始终与 TLS 加密一起使用 Web Sockets 安全来连接到 WebSocket 服务器是最佳的做法,除非你非常确实不会存在中间服务器。虽然这样做的好处是带来了更好的安全,但 TLS 加密确实会增加客户端与服务器两方面的 CPU 消耗,尽管这并非是特别大的增加,而且通过硬件的 SSL/TLS 加速,可以将其减小到几乎可忽略。

让我们再重述一下要点。图 2 展示了在浏览器与 WebSocket 服务器之间建立 WebSocket 连接过程中所做的决定。这张图展示了在 WebSocket(ws://)与 WebSocket 安全(wss://)两种情况中使用显式的和透明的代理服务器建立连接的不同场景。

图 2——代理服务器遍历决策树

图 2 显示了在几乎是最简单的网络拓扑中为什么使用未加密的 WebSocket 连接更容易失败。所有都归结到要理解你所要部署 WebSocket 应用的端到端网络拓扑。一些 HTTP 代理服务器也许会限制端口或者只允许特定的被授权的服务器来访问。在这种场景中使用的 WebSocket 服务器必须被加入到服务器白名单中,连接才有可能成功。 通常,应当使用哪个协议(ws://或是wss://)来进行连接的是由客户端开发者来决定,同时还需要基于 WebSocket 信息流的隐私特征。未来,WebSocket 网关和服务器能够支持在探测到不合作的代理服务器时,动态地升级到 Web Socket 安全。

Kaazing WebSocket 网关和代理服务器

Kaazing WebSocket 网关的特色功能有 Web Socket 模拟,以支持 Web Socket 对所有浏览器可用,包括那些不支持 Web Sockets 的。这一模拟工作在纯粹的 JavaScript 环境,不需要插件,同时还有 Kazzing 独家的 Opportunistic Optimization™技术可以保证最佳的连接环境,无论客户端与中间代理服务器是否支持最新的协议。

例如:如果 Kaazing WebSocket 网关探测到存在 Flash 插件,客户端类库可以充分利用单一的(Flash)TCP 套接字连接的优势,并且,如果直接连接无法实现(例如,如果通讯必须经过防火墙或者一个 HTTP 代理服务器),那么客户端类库仍然可以利用 Flash 运行时的优势,最小化客户端的内存剖析。

如果中间代理服务器位于 Kaazing WebSocket 网关与客户端的中间,那么就会使用一个高度优化的加密流式连接,并且代理 - 服务器 - 感知的网关会自动的转发 HTTP 请求,以使用加密的 HTTP(HTTPS)连接来让流式特性的下行 HTTP 信息流忽略代理服务器。在生产环境中,需要预先考虑到 HTTPS 流可以被用作最坏情况的场景下,以留有一步余地。然而,为了使起动作起来,网关必须被配置适用于 TLS 加密的证书。如果没有的话(这会被视为一个配置错误),Kaazing 将会退回到一种高级的非流式的实现。

kaazing 的 Web Socket 模拟是高度优化的,并且轻而易举的就可以超越大部分的遗留 Comet 解决方案,因为它通过退回到 HTTPS 流保证了最小的延迟,这得归功于 Kazzing 底层对于跨不同源的 HTTP 和 HTTPS 请求的支持。注意不同的退回场景仅被用于模拟模式。使用 Kaazing WebSocket 网关及其 Web Socket 模拟的一个主要好处就是你现在就可以按照 HTML5 Web Sockets 标准来编码,并且这些应用可以运行于所有的浏览器。

负载平衡路由器与防火墙

这一节描述了 HTML5 Web Socket 是如何与负载平衡路由器和防火墙一起工作的。另外,还解释了 Kaazing WebSocket 网关如何带到额外的价值。

HTML5 Web Sockets 与负载平衡路由器

对于这一讨论,让我们首先分清两种不同类型的负载平衡路由器:

Ø TCP(第 4 层)负载平衡路由器以与 HTML5 Web Sockets 很好的工作,因为他们有同样的连接形式:一开始连接一次并保持连接,而不是 HTTP 文档转移的请求——响应式的形式。

Ø HTTP(第 7 层)负载平衡路由器它本是为 HTTP 信息流准备的,因此很容易就会被 WebSocket 升级信息流所迷惑。因此,第 7 层负载平衡路由器需要被配置为能明确感知 WebSocket 信息流。

HTML5 Web Sockets 与防火墙

因为防火墙通常是在对进入的流量控制和出去的流量路由上加上规则(比如,通过代理服务器)因此通常来说没有对于 WebSocket 信息流相关的防火墙顾虑。

kaazing Websocket 网关与负载均衡路由器

第 7 层负载均衡路由器被置于浏览器与多个 WebSocket 服务器之间的关键路径时,有可能会被 WebSocket 协议升级请求搞糊涂。出于这个目的,运行于 Kaazing WebSocket 网关的服务支持伙伴负载均衡器感知,这允许负载均衡路由器被配置为 WebSocket 服务器的伙伴。通过这种方式,负载均衡路由器只需要处理初始的客户端请求并可以发现最佳的活动网关实例来路由信息。

一旦负载均衡路由器选择了网关实例,该网关实例就可以将浏览器转接,直接连接到该活动的网关实例。这意味着负载均衡路由器不介入真正的 WebSocket 流量,由此减少了延迟。然而,出现了硬件失效或网络错误的时候,客户端会自动重连到负载均衡路由器,这将会自动的将那些请求转发到另一个活动的 kaazing 网关实例。

Comet 与代理服务器

最后,让我们来看看 Comet 是如何与代理服务器交互的。因为不存在 Comet 的标准,让我们区别来看两种不同类型的 Comet 实现:长轮询与流式查询。

Ø 长轮询,其实现方式健壮,不会受代理服务器问题的太大影响,因为它仅仅使用 HTTP 请求响应模型。然而,每个请求和响应载体会有许多不必要的 HTTP 头信息开销和延迟。这正是 HTML5 Web Sockets 大展身手的地方——它能够提供 1000:1 的降低不必要的网络流量和延迟。

同时请查阅:我们的桔皮书《 Web Sockets——Web 伸缩性的量变飞跃 》,其中对于 Web Sockets 较遗留的 Comet 解决方案所能带来的显著减少不必要的网络流量提供了详尽的分析。

Ø 流式查询通常比长轮询更高效,因为它在服务器上保持响应开放,并只将关键的数据通过开放连接发送给客户端。然而,这一方案会受到之前提到的代理服务器问题的影响。比如,一个代理服务器可能会缓冲响应而造成延迟。作为替代,代理服务器可以被配置为断开保持了一定时间的 HTTP 连接。这也是为什么大多数遗留 Comet 解决方案简单的使用长轮询的原因。

总结

尽管 HTML5 Web Socket 协议本身并不知晓代理服务器和防火墙,但它具有 HTTP 兼容的握手这一特色,因此 HTTP 服务器可以和 WebSocket 服务器共享它们默认的 HTTP 和 HTTPS 端口(80 和 443)。一些代理服务器于 Web Sockets 无害并且能很好的工作,其它一些有可能妨碍 Web Sockets 正常工作,导致连接失败。在一些情况下可能需要一些额外的代理服务器配置,而一些特定的代理服务器可能需要被升级以支持 Web Sockets。

如果一个浏览器被配置为使用显式的代理服务器(对于加密和未加密的 WebSocket 连接都是),那么在建立 WebSocket 连接时它首先会发出一个 HTTP CONNECT方法到那个代理服务器。

如果使用的是一个未加密的 WebSocket 连接(ws://),那么在透明的代理服务器情况下,浏览器是不知道代理服务器的,所以不会发送 HTTP CONNECT方法。因此,在目前的实践中连接通常会失败。

如果使用的是加密的 WebSocket 安全连接(wss://),那么在透明代理服务器下,浏览器不知道代理服务器,所以不会发出 HTTP CONNECT方法。然而,因为线上信息是加密的,中间透明代理服务器会简单的让加密信息通过,因此使用加密的 WebSocket 连接而成功建立 WebSocket 连接的几率就大大增加了。

kaazing WebSocket 网关是高度优化的,代理感知的 WebSocket 网关,提供原生的 WebSocket 支持和针对上一代浏览器的 Web Socket 模拟功能。如果中间代理服务器被检测到,那么下行 HTTP 信息流就会使用一个高度优化的加密流式连接。这是通过 Kaazing 底层通过跨源的 HTTP 和 HTTPS 请求支持来实现的。在这一方面,不管是原生的或模拟的方式,我们可以通过 WebSocket 网关 100% 的建立 WebSocket 连接。

关于作者

Peter Lubbers 是 Kaazing 的文档和培训主管,掌控文档和培训工作的各个方面,Peter 是 Apress 出版的《Pro HTML5 Programming》的合著者并教授 HTML5 培训课程。是 HTML5 和 WebSocket 的热衷者,他经常在国际活动上公开演讲。

在加入 Kaazing 之前,Peter 是 Oracle 的一名信息架构师,他编写了多本书箱,包括获奖的 Oracle 应用服务器门户配置手册,以及 Oracle 应用服务器微软 Office 交互开发者指南。Peter 同时还开发文档自动化解决方案,并获得了两项发明专利。

在加入 Oracle 之前,Peter 架构并开发了国际化的微软 Office 运用专家测试框架。Peter 同时还是《Pro JSF and Ajax: Building Rich Internet Components》(Apress,2006)这本书的技术审稿人。

查看英文原文 How HTML5 Web Sockets Interact With Proxy Servers


感谢晁晓娟对本文的审校。

给 InfoQ 中文站投稿或者参与内容翻译工作,请邮件至 editors@cn.infoq.com 。也欢迎大家加入到 InfoQ 中文站用户讨论组中与我们的编辑和其他读者朋友交流。

收藏

评论

微博

发表评论

注册/登录 InfoQ 发表评论