sovinchan 2019-06-30
Web Sockets定义了一种在通过一个单一的 socket 在网络上进行全双工通讯的通道。它不仅仅是传统的 HTTP 通讯的一个增量的提高,尤其对于实时、事件驱动的应用来说是一个飞跃。
HTML5 Web Sockets 相对于老的技术(在浏览器中模拟全双工连接的复杂技术)有了如此巨大的提升,以致于谷歌的 Ian Hickson(HTML5 说明书的总编)说:
“将数据的千字节减少到2字节……并将延迟从150ms减少到50ms,这远远超过了边际效应。”事实上,仅这两个因素就足以让谷歌对 Web Sockets 字产生浓厚的兴趣。
让我们来看看 HTML5 Web Sockets 是如何通过与传统的解决方案进行比较,从而极大地减少不必要的网络流量和延迟的
通常,当一个浏览器访问一个网页时,会向拥有这个页面的服务器发送一个HTTP请求。Web 服务器接受这个请求并返回一个响应。
在许多情况下——例如,股票价格、新闻报道、机票销售、交通模式、医疗设备读数等等——浏览器渲染页面时,响应可能已经过时,如果你想获得最新的“实时”信息,你可以不断手动刷新该页面,但这显然不是一个很好的解决方案。
当前尝试提供实时 Web 应用程序其主要围绕轮询和其他服务器端推送技术,其中最引人注目的是 Comet
,它会延迟完成 HTTP 响应以将消息传递到客户端。基于 Comet 的推送一般采用 JavaScript 实现并使用长连接或流等连接策略。
comet: 基于 HTTP 长连接的“服务器推”技术。基于这种架构开发的应用中,服务器端会主动以异步的方式向客户端程序推送数据,而不需要客户端显式的发出请求。Comet 架构非常适合事件驱动的 Web 应用,以及对交互性和实时性要求很强的应用,如股票交易行情分析、聊天室和 Web 版在线游戏等。
通过轮询,浏览器定期发送 HTTP 请求并立即接收响应,这项技术是浏览器首次尝试传递实时信息。显然,如果消息传递的确切时间间隔已知,这是一个很好的解决方案,因为可以在服务器上先把需要发送的信息准备好之后在发送给客户端。然而,实时数据通常是不可预测的,这必然造成许多不必要的请求,因此,在低频率消息的情况下,许多连接被不必要地打开和关闭的。
长轮询是让服务器在接收到浏览器所送出 HTTP 请求后,服务器会等待一段时间,若在这段时间里面服务器有新的消息,它就会把最新的消息传回给浏览器,如果等待的时间到了之后也没有新的消息的话,就会送一个回应给浏览器,告知浏览器消息没有更新。
虽然轮询可以减少产生原本轮询造成网络带宽浪费的情况,但是,如果在资料更新频繁的状况下,长时间轮询不传统比传统的轮询有效率,而且有时候资料量很大时,会造成连续的轮询不断产生,反而会更糟糕。
串流 (streaming) 是让服务器在接收到浏览器所送出的 HTTP 请求后,立即产生一个回应浏览器的连接,并且让这个连接持续一段时间不要中断,而服务器在这段时间内如果有新的消息,就可以透过这个连接将消息马上传送给浏览器。
然而,由于流仍然封装在 HTTP 中,介入的防火墙和代理服务器可能会选择缓冲响应,从而增加消息传递的延迟。因此,如果检测到缓冲代理服务器,流式 Comet 解决方案将退回到长轮询。或者,可以使用TLS (SSL)连接来防止响应被缓冲,但是这种情况下创建和销毁每一个连接将消耗更多的可用的服务器资源。
TLS:安全传输层协议(TLS)用于在两个通信应用程序之间提供保密性和数据完整性。 该协议由两层组成: TLS 记录协议(TLS Record)和 TLS 握手协议(TLS Handshake)。SSL:SSL(Secure Sockets Layer 安全套接层),及其继任者传输层安全(Transport Layer
Security,TLS)是为网络通信提供安全及数据完整性的一种安全协议。TLS与SSL在传输层对网络连接进行加密。
最后,所有这些提供实时数据的方法都会引入 HTTP 请求和响应报头,这些报头包含大量额外的、不必要的报头数据,并会带来延迟。
最重要的是,全双工连接需要的不仅仅是从服务器到客户端的下行连接。为了在半双工HTTP上模拟全双工通信,当今的许多解决方案使用两个连接:一个用于下行,一个用于上行,这两个连接的维护和协调在资源消耗方面引入了大量开销,并增加了许多复杂性。
简单地说,HTTP 不是为实时、全双工通信而设计的,可以在下面的图中看到,该图展示了构建 Comet Web 应用(在半双工的 HTTP 上使用订阅模式实时获取后端数据)的复杂性。
当试图将 Comet 的解决方案扩充系统的规模时会变得更糟。在 HTTP 模拟全双工的浏览器通讯易出错、复杂而且复杂度无法降低。尽管最终用户可能正在体验类似于实时 Web应用程序的服务,但这种 “实时” 体验的代价高得惊人。这个代价是,付出额外的延迟,不必要的网络流量和 CPU性能的影响上。
在 HTML5 规范的通信部分中定义,HTML5 Web Sockets 代表了全双工的网络交互的下一个演变 —— 一个全双工、双向的通信通道,通过 Web 上的单个套接字进行操作。
HTML5 Web Sockets 提供了一个真正的标准,可以使用它来构建可扩展的实时 Web 应用程序。此外,由于它提供了浏览器本地的套接字,因此避免了 Comet 解决方案容易出现的许多问题。 Web Socket s移除了开销大幅度减轻了复杂度。
为了建立WebSocket连接,客户端和服务器在首次握手时从 HTTP 协议升级到 WebSocket 协议,如下图所示:
示例1 - WebSocket握手(浏览器请求和服务器响应)
熟悉 HTTP 的可能会发现,这段类似 HTTP 协议的握手请求中,多了几个东西:
Upgrade: websocket Connection: Upgrade
这个就是 Websocket 的核心了,告诉 Apache
、 Nginx
等服务器:发起的是 Websocket协议,使用对应的Websocket协议处理,而不是使用 HTTP 协议。
一旦建立,WebSocket 数据帧可以在客户端和服务器之间以全双工模式来回发送。文本和二进制帧都可以发送全双工,在同一时间向任意方向发送,数据的最小帧只有两个字节。
在文本帧的情况下,每个帧以 0x00
元组开头,以 0xFF
元组结束,中间包含 UTF-8
数据,WebSocket 文本帧使用终止符,而二进制帧使用长度前缀。
注意:尽管 Web Sockets 协议已经准备好支持各种客户端集,但是它不能将原始二进制数据交付给 JavaScript,因为 JavaScript 不支持字节类型。因此,如果客户端是 Javascript 的,二进制数据将被忽略 —— 但是可以把它发送给其它支持二进制的客户端。
那么在非必要的网络传输和延迟性上究竟减少了多少?让比较一下长连接应用和 WebSocket 应用。
对于轮询示例,我创建了一个简单的 Web 应用程序,其中 Web 页面使用传统的发布/订阅模型从RabbitMQ 消息队列中获取实时股票信息。
它通过轮询驻留在 web 服务器上的 Java Servlet
来实现这一点。RabbitMQ 消息队列从虚构的持续改变股票价格的股票价格服务接收数据。
Web 页面连接并订阅特定的股票通道(message broker上的主题),并使用 XMLHttpReques t每秒轮询更新一次。当接收到更新时,执行一些计算,股票数据显示在一个表中,如下图所示。
注意:后台股票服务实际上每秒会产生大量股票价格更新,因此每秒轮询一次实际上比使用Comet
长轮询解决方案更为谨慎,后者会导致一系列持续轮询,这里轮询有效的节制了数据更新。
这一切看起来都很好,但从内部看,这个应用程序有一些严重的问题。在 Mozilla Firefox 中使用 Firebug
(一个火狐插件——可以对网页进行deb、跟踪加载页面和执行脚本的时间),可以看到 GET 请求每隔一秒就会连接服务器。打开Live HTTP Headers(另外一个火狐插件——可以显示活跃 HTTP 头传输)暴露了每一个连接上巨大数量的头开销(header overhead)。下面的例子展示了一个请求和响应的头信息。
事例二:HTTP request header
事例三:HTTP response header
只是为了好玩,我数了数所有的字符。总的 HTT P请求和响应头信息开销包含 871
字节,甚至不包括任何数据 !当然,这只是一个示例,可以拥有少于 871
字节的头数据,但是我也看到过头数据超过 2000
字节的情况。
在这个示例应用程序中,典型股票标题信息仅仅20个字符长。正如所看到的,它实际上被过多的头信息淹没了,而头信息甚至在一开始就不是必需的!
那么当你把这个应用部署到大用户量的场景下会怎么样? 让我们在三个不同的场景中对比与此轮询应用程序关联的 HTTP 请求和响应头数据的网络吞吐量。
1000
个客户端轮询,每秒的网络流量是 6.6 M
。10000
个客户端轮询,每秒的网络流量是 66 M
。100000
个客户端轮询,每秒的网络流量是 660 M
。这是大量不必要的网络吞吐量,要是我们能通过网络得到必要的数据就好了,此时就可以使用 HTML5 Web Sockets!
我重新构建了应用程序以使用 HTML5 Web Sockets,在 Web 页面中添加了一个事件处理程序来异步侦听来来自于代理的股票更新信息。
。每一个信息都是一个WebSocket帧,只有两个字节的开销(而不是871字节)! 看看这如何影响我们的三个用例中的网络吞吐量开销。
1000
个客户端轮询,每秒的网络流量是 0.015 M
。10000
个客户端轮询,每秒的网络流量是 0.153 M
。100000
个客户端轮询,每秒的网络流量是 .1526 M
。如下图所示,与轮询解决方案相比,HTML5 Web Sockets大大减少了不必要的网络流量。
那么延迟的减多少呢? 请看下图:
在上半部分,可以看到半双工轮询解决方案的延迟。在本例中,假设消息从服务器传输到浏览器需要50
毫秒,那么轮询应用程序将引入大量额外的延迟,因为在响应完成时必须将新请求发送到服务器。这个新请求需要另一个50ms
,在此期间服务器不能向浏览器发送任何消息,从而导致额外的服务器内存消耗。
在图的下半部分,可以看到 WebSocket 解决方案降低了延迟。一旦连接升级到 WebSocket,消息就可以在到达时从服务器流到浏览器。消息从服务器传输到浏览器仍然需要 50
毫秒,但是WebSocket 连接仍然打开,因此不需要向服务器发送另一个请求。
HTML5 Web Sockets 在实时网络的扩展性上向前迈出了一大步。正如在本文中看到的, HTML5 Web Sockets可以提供 500:1
甚至 1000:1
的非必要HTTP头信息传输的变少,以及 3:1
延迟性的降低。这不仅仅是个进步,它是巨大的一个飞跃!
原文:
http://www.websocket.org/quan...
你的点赞是我持续分享好东西的动力,欢迎点赞!