HTTP连接时HTTP报文传输的关键通道,这部分内容主要介绍下面几方面的内容
- HTTP是如何通过TCP连接的
- TCP连接的时延、瓶颈以及存在的障碍
- HTTP的优化,包括并行连接、keep-alive(持久连接)和管道化连接
- 管理连接时应该以及不应该做的事
TCP连接
几乎所有的HTTP通信都是由TCP/IP承载的,下图是浏览器处理 http://www.joes-hardware.com:80/power-tools.html 这个URL的过程
HTTP连接实际上就是TCP连接及其使用规则。
上图清晰地表示了几个协议之间的层次关系
HTTP要传送一条报文的时候,会以流的形式将报文数据的内容通过一条打开的TCP连接按序传输。TCP接收到数据流之后,会将数据流分成被称作段的小数据块,并将数据块封装在IP分组中,通过因特网进行传输。
任意时刻计算机都可以有几条TCP连接处于打开状态,TCP是通过端口号来保持所有这些连接持续不断地运行
IP地址可以将你链接到正确的计算机,而端口号可以将你链接到正确的应用程序上去,TCP连接时通过4个值来识别的
<源IP地址 ,源端口号,目的IP地址,目的端口号>
TCP性能相关
HTTP事务的时延
下图是一个串行HTTP事务的时间线
从图中可以看出,与建立TCP连接,以及传输请求和响应报文的时间相比,事务处理时间可能是很短的。除非客户端或者服务器超载,或正在处理复杂的动态资源,否则HTTP时延是由TCP网络时延构成的。
HTTP事务时延有以下几种主要原因:
- 客户端首先需要根据URL确定Web服务器的IP地址和端口号,如果最近没有对URL中的主机名进行访问,那么DNS将URL中的主机名转换为IP地址可能会花费数十秒的时间。如果是近期访问过的主机名,那么在HTTP客户端的DNS缓存中,就会保存该主机名对应的IP地址。
- 接下来,客户端会向服务器发送一条TCP连接请求,并等待服务器回送一个请求接受应答。每条新的TCP连接都会有连接新建时延,这个时间虽然很短,但是如果一次性新建多条TCP连接,那么这个时延叠加起来就很长了。
- 一旦连接建立起来之后,客户端就会通过新建的TCP信道来发送HTTP请求,数据到达时,web服务器会从TCP链接中读取请求报文,并处理。因特网传输请求报文以及服务器处理请求报文都需要时间
- 然后,服务器会回送HTTP响应,这也需要花费时间。
TCP连接的握手时延
建立一条新的TCP连接时,甚至是在发送任意数据钱,TCP软件之间会交换一系列的IP分组,对连接的有关参数进行沟通。如果连接只用来传送少量的数据,这些交换过程就会严重降低HTTP的性能。
TCP握手需要经过以下几个步骤:
- 请求新的TCP连接时,客户端要服务器发送一个小的TCP分组,这个分组中设置了一个特殊的SYN标记,说明这是一个连接请求。
- 如果服务器接收了连接,就会对一些连接参数进行计算,并向客户端回送一个TCP分组,这个分组中的SYN和ACK标记都被置位,说明连接请求已被接受
- 最后,客户端向服务器回送一条确认信息,通知它连接已成功建立。现代的TCP栈都允许客户端在这个确认分组中发送数据
一般的小的HTTP事务可能会在TCP建立上花费50%,或更多的时间,后面会讨论HTTP如何通过重用现存的连接来较少新建TCP时所造成的时延。
延迟确认
每个TCP段都有一个序列号和一个数据完整性校验和。每个段的接收者收到完好的段时,都会向发送者回送一个小的确认分组。如果发送者没有在指定的窗口时间内收到确认信息,发送者就会认为分组已被破坏或损毁,并重发数据。
由于确认报文很小,所以TCP允许服务器在发往客户端的或者是客户端发往服务器的数据分组中队其进行“捎带”,将返回的确认信息和输出的数据分组结合在一起,更有效地利用网络。
为了增加确认报文找到同向传输数据分组的可能性,很多TCP栈都实现了一种“延迟确认”的算法。延迟确认算法会在一个特定的窗口时间爱你(通常是100~200ms)内将输出确认放在缓冲区中,以寻找能够捎带它的输出分组。如果在时间段内没有输出分数符合条件,那么确认信息就放到单独的分组中进行传送。
TCP慢启动
TCP连接会随着时间进行自我调谐,起初会限制连接的最大速度,如果数据成功传输,会随着时间的推移提高传输的速度。这种调谐被称为TCP慢启动,用于防止因特网的突然过载和拥塞。
TCP慢启动限制了一个TCP端点在任意时刻可以传输的分组数。简单来说,每成功接收一个分组,发送端就有了发送另外两个分组的权限。当一个HTTP事务由大量数据要发送的时候,是不能一次性将所有分组都发送出去的,必须先发送一个分组,等待确认,然后可以发送两个分组,每个分组都必须被确认,这样就可以发送4个分组了,一次类推。这种方法被称为“打开拥塞窗口”。
HTTP连接的处理
Connection头部
在某些情况下,两个相邻的HTTP应用程序会为它们共享的连接应用一组选项,HTTP的connection头部字段中由一个由逗号隔开的连接标签列表,这些标签为此连接指定了一些不会传播到其他连接中去的选项。
Connection头部可以承载3中不同的标签:
- HTTP头部字段名,列出了只与此连接有关的头部
- 任意标签值,用于描述此链接的非标准选项
- 值close,说明操作完成之后需关闭这条持久连接
在转发报文之前,必须删除connection头部列出的所有头部字段。由于connection头部可以防止无意中对本地首部的转发,因此将逐跳头部名放入connection首部被称为“对头部的保护”。
串行事务处理时延
一张图就可以看出串行事务的耗时是多么严重
并行连接
如下图所示,客户端打开多个TCP连接,并行地执行多个HTTP事务。
下图示上图的时间
可以看出比前面的串行的要快很多。
但是,并行其实并行其实不一定就更快,当客户端的网络带宽不足的时候,大部分的时间可能是用来传送数据的。在这种情况下,一个连接到速度较快的服务器上的HTTP事务就会很容易耗尽所有可用的带宽,如果并行加载多个对象,每个对象都去竞争有限的带宽,每个对象都会以较慢的速度按比例加载,这样带来的性能提升很少,甚至没什么提升。
如果需要并行加载上百个对象,那么客户端就要打开上百个连接,这样肯定是很慢的。实际上,浏览器确实使用了并行连接,但是它们会将并行连接的总数限制为一个较小的值。服务器也可以随意关闭来自特定客户端的超量连接。
持久连接
HTTP/1.1(以及HTTP/1.0的各种增强版本)允许HTTP设备在事务处理结束后将TCP连接保持在打开状态,以便为未来的HTTP请求重用现存的连接。在事务结束之后仍然保持打开状态的TCP连接被称为持久连接。
重用已对目标服务器打开的空闲持久连接,就可以避开缓慢的连接建立阶段。而且,已经打开的连接还可以避免慢启动的拥塞适应阶段,以便更快速地进行数据的传输。
前面说到的并行连接有明显的缺点,比如新建每一条连接都需要耗费时间和带宽,TCP的慢启动应用以及可并行数量的限制等等
持久化连接和并行连接相配合使用是比较理想的。很多web应用程序都会打开少量的并行连接,其中的每一个都是持久连接。
持久连接有两种类型:比较老的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,会发回响应报文之后关闭连接。
代理或网关必须在将报文转发出去或将其高速缓存之前,删除在connection首部中命名的所有首部字段以及connection首部自身。
HTTP/1.1持久连接
与HTTP/1.0的keep-alive连接不同,HTTP/1.1持久连接在默认情况下是激活的,除非特别说明,否则HTTP/1.1假定所有连接都是持久的。要在事务处理结束之后将连接关闭,HTTP/1.1应用程序必须向报文中显式地添加一个connection:close首部。
但是客户端和服务器仍然可以随时关闭空闲的连接。不发送connection:close并不意味着服务器承诺永远将连接保持在打开状态。
管道化连接
HTTP/1.1允许在持久连接上可选地使用请求管道,这是相对于keep-alive连接的又一性能优化。在响应到达之前,可以将多条请求放入队列。当第一条请求通过网络流向地球另一端的服务器时,第二条和第三条请求也开始发送了。在高时延网络条件下,这样做可以降低网络的环回时间,提高性能。
下图是几种方式的比较