原文地址:<a href=“http://muchstudy.com/2016/08/20/HTTP连接管理/” target="_blank">HTTP连接管理</a>
曾经有同事给我说有客户问她我们的产品是否支持长连接,听到这问题,我告诉她长连接可以通过websocket来实现,考虑到目前的浏览器兼容性,大部分时候采用客户端轮询来曲线救国。当了解到HTTP的keep-alive时,不禁回想起这件事,当初我的回答可能并不是客户想要的的答案!
下面,分享一下HTTP连接的一些情况,所有的截图均来自《HTTP权威指南》。想更详细了解的可以直接去看这本书的第四章,更深入的可以去翻翻RFC文档以及TCP/IP协议。有大部头恐惧症,以及只希望能有个初步以及概要性的了解的,接下来的内容很适合您!
一、HTTP连接的过程
上图为从客户端-服务器端-客户端交互的整个过程。接下来介绍在这个交互过程中影响性能因素的几个关键点。
二、连接中的性能聚焦区域
HTTP事务的时延
HTTP 事务的时延有以下几种主要原因:
- 客户端首先需要根据URI确定Web服务器的IP地址和端口号。
- 接下来,客户端会向服务器发送一条 TCP 连接请求, 并等待服务器回送一个请求接受应答。
- 一旦连接建立起来了,客户端就会通过新建立的 TCP 管道来发送 HTTP 请求。
- 最后,Web 服务器会回送 HTTP 响应。
这些TCP网络时延的大小取决于硬件速度、网络和服务器的负载,请求和响应报文的尺寸,以及客户端和服务器之间的距离。
TCP连接的握手时延
TCP连接的握手时延对应于上图的建立连接部分。发送任意数据,当新建立一条TCP连接时,都会走一下如下握手过程。如果连接只用来传送少量数据,这些握手过程就会严重降低 HTTP 的性能。
TCP 连接握手需要经过以下几个步骤:
- 请求新的 TCP 连接时, 客户端要向服务器发送一个小的 TCP 分组( 通常是 40 ~60 个字节)。 这个分组中设置了一个特殊的 SYN 标记,说明这是一个连接请求。(a部分)
- 如果服务器接受了连接,就会对一些连接参数进行计算,并向客户端回送一个TCP 分组,这个分组中的 SYN 和 ACK 标记都被置位,说明连接请求已被接受。(b部分)
- 最后, 客户端向服务器回送一条确认信息, 通知它连接已成功建立(c部分)
这部分对用户不可见
TCP慢启动
TCP 连接会随着时间进行自我“ 调谐”, 起初会限制连接的最大速度, 如果数据成功传输, 会随着时间的推移提高传输的速度。 这种调谐被称为 TCP 慢启动( slow start), 用于防止因特网的突然过载和拥塞。 由于存在这种拥塞控制特性, 所以新连接的传输速度会比已经交换过一定量数据的、“ 已调谐” 连接慢一些。
三、HTTP连接的处理
1.并行连接
试想,当浏览器加载一个页面时,如果一个个HTTP请求都串行执行,那显示出这个页面将会奇慢无比。
下面用两个图来做个对比:
打开大量连接会消耗很多内存资源,从而引发自身的性能问题。复杂的Web页面可能会有数十或数百个内嵌对象。客户端可能可以打开数百个连接,但 Web 服务器通常要同时处理很多其他用户的请求, 所以很少有 Web 服务器希望出现这样的情况。 一百个用户同时发出申请, 每个用户打开 100 个连接, 服务器就要负责处理10000个连接。 这会造成服务器性能的严重下降。
实际上,浏览器确实使用了并行连接,但它们会将并行连接的总数限制为一个较小的值(通常是4个)。之前看baidu的webuploader的设计文档,经过测试,当文件分片上传时,建议开始的线程为3个。 地址在<a href=“http://fex.baidu.com/blog/2014/04/html5-uploader/?qq-pf-to=pcqq.c2c” >这里</a>,更详细的在<a href=“https://github.com/fex-team/webuploader/tree/research/experiment” >这里</a>,上面的目录里也还有一些
2.持久连接
HTTP/1.1(以及 HTTP/1.0 的各种增强版本)允许 HTTP 设备在事务处理结束之后将 TCP 连接保持在打开状态, 以便为未来的 HTTP 请求重用现存的连接。 在事务处理结束之后仍然保持在打开状态的 TCP 连接被称为持久连接。 非持久连接会在每个事务结束之后关闭。持久连接会在不同事务之间保持打开状态, 直到客户端或服务器决定将其关闭为止。
持久连接降低了时延和连接建立的开销,将连接保持在已调谐状态, 而且减少了打开连接的潜在数量。
持久连接有两种类型:
- HTTP/1.0+ “keep-alive”连接
- HTTP/1.1 “persistent”连接
HTTP/1.0+ keep-alive连接
实现 HTTP/1.0 keep-alive 连接的客户端可以通过包含 Connection: Keep-Alive
首部请求将一条连接保持在打开状态。
如果服务器愿意为下一条请求将连接保持在打开状态,就在响应中包含相同的首部。如果响应中没有 Connection: Keep-Alive 首部, 客户端就认为服务器不支持 keep-alive, 会在发回响应报文之后关闭连接。
上图中将在串行连接上实现 4 个HTTP 事务的时间线与在一条持久连接上实现同样事务所需的时间线进行了比较。由于去除了进行连接和关闭连接的开销, 所以时间线有所缩减。
keep-alive选项:
例如:Keep-Alive: max=5, timeout=120
- 参数 timeout 是在 Keep-Alive 响应首部发送的。 它估计了服务器希望将连接保持在活跃状态的时间。 这并不是一个承诺值。
- 参数 max 是在 Keep-Alive 响应首部发送的。 它估计了服务器还希望为多少个事务保持此连接的活跃状态。 这并不是一个承诺值。
- Keep-Alive 首部还可支持任意未经处理的属性, 这些属性主要用于诊断和调试。语法为 name [=value]。
在 HTTP/1.0 中,keep-alive 并不是默认使用的。客户端必须发送一个
Connection: Keep-Alive
请求首部来激活 keep-alive 连接。
HTTP/1.1 persistent连接
HTTP/1.1 逐渐停止了对 keep-alive 连接的支持, 用一种名为持久连接(persistent connection) 的改进型设计取代了它。
与 HTTP/1.0+ 的 keep-alive 连接不同, HTTP/1.1 持久连接在默认情况下是激活的。 除非特别指明, 否则 HTTP/1.1 假定所有连接都是持久的。 要在事务处理结束之后将连接关闭, HTTP/1.1 应用程序必须向报文中显式地添加一个Connection:close
首部。 这是与以前的 HTTP 协议版本很重要的区别, 在以前的版本中, keep-alive 连接要么是可选的,要么根本就不支持.
这种连接相当于是在HTTP/1.1之上默认开启keep-alive
3.管道化连接
HTTP/1.1 允许在持久连接上可选地使用请求管道。 这是相对于 keep-alive 连接的又一性能优化。 在响应到达之前, 可以将多条请求放入队列。当第一条请求通过网络流向地球另一端的服务器时, 第二条和第三条请求也可以开始发送了。 在高时延网络条件下, 这样做可以降低网络的环回时间,提高性能。
四、总结
随着HTTP协议版本以及浏览器版本的不断升级更新,以上这些针对HTTP连接性能优化的选项基本上都实现了自动化、智能化。所以,大家了解了解就好,被人问到避免出现我之前的尴尬境况。
把这块弄清楚并整理出来后,强迫症减轻了不少…