《HTTP权威指南》笔记:第四章-连接管理

一、TCP连接

世界上几乎所有的HTTP通信都是TCP/IP承载的,TCP/IP是全球计算机及网络设备都在使用的一种常用的分组交换网络分层协议集。

1.1 TCP的可靠数据管道

TCP连接是因特网上的可靠连接,为HTTP提供了一条可靠的比特传输管道,从TCP连接一段填入的字节会从另一端以原有的顺序正确的传送出来。

1.2 TCP流是分段的,由IP分组传送

TCP的数据通过名为IP分组IP数据报)的小数据块来发送的。HTTP要发送一条报文时会以流的形式使用TCP连接传输,TCP接收到数据流之后将其进行截断分组封装到IP分组内。每个IP分组由以下几部分组成:

  • 一个IP分组首部(通常为20字节),包括源IP地址和目的IP地址,长度以及一些其他的标记;
  • 一个TCP段首部(通常为20字节),包括TCP端口号,TCP控制标记以及用于数据排序和完整性检查的数据;
  • 一个TCP数据块。

HTTP模型:HTTP -> TCP -> IP...

HTTPS模型:HTTP -> TLSSSL -> TCP -> IP...

1.3 保持TCP连接的正常运行

TCP连接时通过4哥值来识别的,这四个值一起唯一定义一条连接:

<源IP 源端口 目标IP 目标端口>

这其中任一值不同都不能称为同一个连接。

二、对TCP性能的考虑

HTTP位于TCP上层,以所HTTP事务性能再很大程度上取决于底层TCP通道的性能。

2.1 HTTP事务的时延

一般来说,在整个HTTP事务周期内,服务端对事务的处理所占时间可能是最短的(除非服务端超载或者处理复杂的动态资源),所以大部分时延都是由TCP网络时延构成的。

HTTP事务时延有以下几种原因:

  1. DNS查询,可能最多会花费数十秒时间。
  2. 建立TCP连接,一个TCP连接需要SYN/ACK+SYN三次报文传输才能建立,但是第三次的IP数据报中可以装载HTTP报文了。
  3. 服务器处理事务,连接建立后,服务器接收到客户端的报文,然后开始处理,处理事务也会花费一定时间。
  4. 网络传输,服务端返回结果在网络中传输需要一定时间。

从以上原因可以看出,HTTP时延的大小取决于:硬件速度,网络和服务器负载,报文尺寸,以及端到端的距离,还有TCP协议产生的时延。

2.2 TCP产生时延的总结

  • TCP连接握手
  • TCP慢启动
  • 数据聚集的Nagle算法
  • 用于捎带确认的TCP延迟确认算法
  • TIME_WAIT时延和端口耗尽

2.3 握手时延

众所周知建立TCP连接需要三次握手:

  1. 客户端发送一个TCP分组(40~60字节),分组中设置了SYN标记。
  2. 服务端接收连接之后,对连接参数进行计算,并把TCP分组的SYNACK都置位。
  3. 最后客户端向服务端返回一条确认TCP分组,其中ACK被置位,并且在这个分组中可以添加HTTP报文。

2.4 延迟确认

TCP为了确保可靠传输,实现了确认机制,及每个TCP段都有一个序列号和数据完整性校验和,段的接收者在收到完好的段后会会向发送者回复一个确认TCP分组,如果发送者没有在指定窗口时间内收到确认分组就会认为发送失败并且会重新发送数据。

由于确认分组很小,所以TCP会允许在发往同方向的分组中对其进行捎带,为了增加捎带的可能,TCP实现了一种延迟确认的算法。即将确认信息缓存下来在特定时间窗口内(100 ~ 200ms)如果有可以捎带的TCP分组,那么从缓存中取出来确认信息,然后进行捎带。如果在这个窗口内没有同方向的TCP分组,那么就需要创建一个TCP分组单独传输确认信息。

2.5 TCP慢启动

TCP连接刚建立后,传输速率会被限制,然后随着时间的推移慢慢提高传输速率,主要用于防止网络突然过载。

2.6 Nagle算法

一个IP分组最小都携带了40字节大小的首部,如果被传输的数据很少的话,那么TCP的效率就想当低了,所以Nagle算法鼓励发送全尺寸的段(因特网上一般为几百字节),所以当有TCP段还没有收到确认分组时,也会被缓存起来,和新的数据凑在一起凑够全尺寸再发送出去。Nagle算法和TCP的延迟确认机制存在一定的冲突。。。所以大部分HTTP应用会设置TCP_NODELAY来禁用Nagle算法。

2.7 端口耗尽

当某个TCP连接关闭时,会在内存中维护一个小的控制块,用来记录关闭连接端口和IP地址,时间为最大分段使用时间的2倍(2MSL,通常为2分钟)。在这段时间内,该端口不能再建立重复的连接,而端口时有上限的,所以连接速率被限制在60000/120 = 500 次/秒

三、HTTP连接

提高HTTP连接性能的技术:

  • 并行连接,通过多条TCP同时发起HTTP事务。
  • 持久连接,重用TCP连接。
  • 管道化连接,通过共享的TCP连接发起并发HTTP请求。
  • 复用的连接,交替传送请求和响应的报文。

四、持久连接

HTTP/1.1允许HTTP客户端在事务处理结束之后将TCP连接保持在打开状态,以便为未来的HTTP请求重用现存的连接。

在事务处理结束后仍然保持在打开状态的TCP连接被称为持久连接。

持久连接有两种类型:HTTP/1.0+keep-aliveHTTP/1.1persistent

4.1 使用keep-alive

客户端发送HTTP请求时需加首部connection:keep-alive,如果服务器支持keep-alive,并且同意了持久连接,那么响应的首部也会加入connection:keep-alive,如果服务端不同意持久连接,那么就不会返回这个首部,并且在请求结束后会关闭空闲的TCP连接。响应首部中还会有keep-alive首部对持久连接进行描述:

  • timeout,服务器预期保持活跃时间。
  • max,估计服务器能为多少个HTTP事务保持活跃连接。
  • 扩展值,一般为name[=value],多个在值以逗号隔开。

4.2 使用keep-alive的限制

  • 客户端必须自主发送connection:keep-alive来激活持久连接。
  • 服务端必须也返回connection:keep-alive才表示激活了持久连接,否则服务端随时会关闭该连接。
  • 使用持久连接的HTTP事务必须在在首部写入正确的content-length,则否TCP无法区分该TCP段属于哪一个HTTP事务。
  • 代理和网关在缓存持久连接的请求时需要删除connection首部。
  • 服务端应当忽略HTTP/1.0及以下协议的connection,因为他们不支持持久化连接,这个首部可能是误转发的。

4.3 keep-alive哑代理

某些代理服务器可能根本不支持keep-alive,但是它转发的时候没有删除客户端的connection:keep-alive,导致服务端以为它想建立持久连接,于是服务端返回keep-alive,代理虽然不知道这个首部是什么意思,于是又转发给了客户端,客户端收到keep-alive后以为服务器同意建立持久连接,但是在复用TCP连接的时候,代理服务器根本不支持持久连接,所以请求就会被挂起。

4.4 proxy-connection

Netscape的浏览器使用proxy-connection替代connection,当低版本代理转发该请求时,直接转发到服务器,服务器不认识该首部,于是直接忽略,但是遇到支持该首部的代理,会把该首部再换成connection,便可以与服务器正常建立持久连接了。

4.5 HTTP/1.1 persistent持久连接

不像HTTP/1.0必须主动声明持久连接,在HTTP/1.1中默认都使用持久连接,除非主动在首部中加入connection:close表示短链接。

4.6 使用persistent

  • 客户端发送connection:close后就无法再使用这个TCP连接了。
  • keep-alive一样,使用持久连接的HTTP请求需要正确的content-length或者使用分块传输编码。
  • HTTP/1.1的代理不应该与HTTP/1.0的客户端建立长连接。
  • 一个客户端最多与服务器维持2条长连接。

4.7 管道化连接

HTTP/1.1允许在持久连接上使用请求管道,即在响应到达之前,可以将多条请求放入队列,当第一条请求发出之后,第二条第三条等也可以逐个开始发送了。这样做的限制是:

  • 如果客户端无法确认当前使用的是否持久连接,那么就不应该使用管道。
  • 必须按照与请求相同的顺序交付HTTP响应,因为HTTP没有序号标签。
  • 客户端需要做好处理请求失败的处理,因为长连接随时会关闭。
  • 不允许使用管道发送POST等非幂等请求,否则不知道失败后重新请求是否会造成严重影响。

五、关闭连接的奥秘

5.1 任意关闭连接

对于持久连接来说,服务器在该连接的空闲时间的任意时刻都可能关闭该连接,而我们知道HTTP请求可能被分为多个TCP段,那么可能造成一个HTTP请求的部分数据传输出现问题?客户端需要做好错误重传处理!

5.2 重试的幂等性

前面说过,持久连接任何时刻都可能会被关闭,尤其是使用了管道化传输的,在队列中积压了大量的请求,在请求失败后,如果客户端尝试重新打开连接传输,对于非幂等请求可能造成很大的副作用,那么如POSTPUTDELETE等请求方法就不应该使用持久连接管道化传输。

参考文献

[1]《HTTP权威指南》

你可能感兴趣的:(《HTTP权威指南》笔记:第四章-连接管理)