HTTP权威指南笔记之连接管理

时间:2019-09-29
本文章向大家介绍HTTP权威指南笔记之连接管理,主要包括HTTP权威指南笔记之连接管理使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

纲要:

  • HTTP是如何使用TCP连接的;
  • TCP连接的时延、瓶颈、存在的障碍;
  • HTTP的优化,并行、keep-alive/持久连接、管道化连接;
  • 管道连接时应该及不应该做的事情

TCP连接

TCP为HTTP提供了一条可靠的数据传输管道。一旦连接建立,客户端和服务器之间交换的报文就永远不会丢失、受损或失序(在连接没有断开的情况下)
TCP流是分段的、由IP分组传送:
TCP的流会被分成小块(段segment)组装成IP分组(或IP数据报)来发送。

每个IP分组包括三部分:IP分组首部(20字节,包含源和目的ip地址,及其他标记),TCP段首部(20字节,包含tcp端口号,控制标记,及用于排序和完整性校验的值),TCP数据块(0或多个字节)。
所以要注意,比如TCP流是1字节,比如分成1个段,但需要传输的字节数在41+以上。试想如果有很多个TCP流都是特别小的数据量,那么反而会对带宽造成很大的浪费。

TCP是通过端口号来保持连接的,IP地址是用来寻址计算机,端口号是用来寻找计算机上对应应用程序的。
TCP连接的唯一标识,包含4个值:源ip、源端口、目的ip、目的端口

TCP套接字编程:

对TCP性能的考虑

HTTP事务的时延:

TCP网络时延的大小取决于硬件速度、网络、服务器负载、请求和响应的尺寸、客户端与服务器间的距离。
常见的TCP时延包括:

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

1、TCP连接的握手时延

从图中可以看出,小的HTTP事务可能会在TCP建立上花费50%甚至更多的时间。

2、延迟确认(ACK延迟机制)
背景:由于因特网无法确保可靠的分组传输(路由器超负荷的话,可以随意丢弃分组),所以TCP实现了自己的确认机制来确保数据的成功传输。(TCP是可靠的)
每个TCP段都有一哥序列号和数据完整性校验和。每个段的接受者收到时都会向发送者回送小的确认分组。入股发送者没有在指定的时间窗内收到确认信息,发送者就认为分组已被损坏,并重发数据。
但是由于确认报文很小,所以TCP允许在发往相同方向的输出数据分组中对其进行捎带.TCP将返回的确认信息与输出的数据分组结合在一起,以便更有效地利用网络。为了实现这一可能,出现了”延迟确认算法”
延迟确认算法会在一个特定的时间窗(100~200ms)内将输出确认存放在缓冲区中,以便寻找能够捎带它的输出数据分组。但是,HTTP具有双峰特征的请求-应答行为降低了捎带信息的可能。当希望有相反方向回传分组的时候,偏偏没有那么多。这样延迟确认算法就会引入相当大的时延。根据操作系统不同,可以调整或禁用延迟确认算法.(禁用之前一定要确保不会引发这些算法所要避免的问题)
(其实在建立连接第三次握手时,客户端向服务器端发送ACK包时就已经可以携带数据)

3、TCP慢启动
TCP连接会随着时间进行自我调谐,起初会限制连接的最大速度,如果数据成功传输,会随着时间的推移提高传输的速度。这种调谐被称为TCP慢启动(slow start),用于防止因特网的突然过载和拥塞
TCP慢启动限制了一个TCP端点在任意时刻可以传输的分组数。(除非随着时间推移打开拥塞窗口后)
因为慢启动的存在,出现了HTTP持久连接,在持久连接中,后续的请求都会在处于已调谐的连接中进行。

4、Nagle算法和TCP_nodelay
前面说过,每个IP分组中都至少装载了40个字节的标记和首部,所以如果TCP发送了大量包含少量数据的分组,,网络的性能会严重下降(发送大量单字节分组的行为称为发送端傻窗口综合症)。
Nagle算法试图在发送一个分组之前,将大量TCP数据绑定在一起,以提高网络效率。它鼓励发送全尺寸(LAN最大尺寸分组为1500字节,因特网上是几百字节)的段,只有当所有其他分组都被确认之后,Nagle算法才允许发送非全尺寸的分组。只有当挂起分组被确认,或者缓存中积累了足够发送一个全尺寸分组的数据时,才会将缓存的数据发送出去。
这样在HTTP中会引发性能问题:
如果小的HTTP报文可能无法填满一个全尺寸分组,从而导致因为等待那些永远不会到来的额外数据而产生时延。
Nagle算法就是为了尽可能发送大块数据,避免网络中充斥着许多小数据块。
Nagle算法的基本定义是任意时刻,最多只能有一个未被确认的小段。 所谓“小段”,指的是小于MSS尺寸的数据块,所谓“未被确认”,是指一个数据块发送出去后,没有收到对方发送的ACK确认该数据已收到。

Nagle算法与延迟确认之间的交互也会存在问题。
故而HTTP应用通常会在自己的栈中设置参数TCP_NODELAY,禁用Nagle算法,以提高性能。
当然,如果你要用Nagle算法的话,一定要确保会向TCP写入大块的数据,这样就不会产生一堆小分组了。

更多参考,这篇文章介绍的也不错:http://blog.csdn.net/historyasamirror/article/details/6423235

5、TIME_WAIT累积与端口耗尽
TIME_WAIT端口耗尽是很严重的性能问题。当某个TCP端点关闭TCP连接时,会在内存中维护一个小的控制块,用来记录最近所关闭连接的IP地址和端口号。这类信息只会维持一小段时间,即2MSL(最大分段使用期的两倍,以前为2分钟,但由于硬件及带宽的提升,这个时间变小了很多,而且因操作系统不同而不同,我在docker下看了ubuntu的MSL值为60),以确保在这段时间内不会创建具有相同地址和端口号的新连接

Linux下查看并修改MSL(Maximum Segment Lifetime)值

1
2
3
4
5
6
7
sysctl net.ipv4.tcp_fin_timeout

cat /proc/sys/net/ipv4/tcp_fin_timeout
修改
edit /proc/sys/net/ipv4/tcp_fin_timeout
使上面修改永久生效
sysctl -p /etc/sysctl.conf

需要注意的是,超过2MSL后,有分组被复制的可能性。试想,如果来自之前连接的分组插入了具有相同连接值的新TCP流,会破坏TCP数据。
试想只有一个客户端与一个服务端的情形下的压力测试,前面我们知道,构建一个TCP连接需要的4个值:源ip,源端口,目的ip,目的端口。这里面唯一可以改变的只有源端口。
而源端口可用数量有限(最大65000左右,可用一般6w个左右),而且在2MSL秒(比如60s)内连接是无法重用的,连接率就被限制在了60000/60=1000次/s。当达到此峰值,那么客户端的端口也相应地被耗尽了。
注意:即使没有遇到端口耗尽问题,也要特别小心有大量连接处于打开状态的情况,这是有些操作系统的速度会严重减缓。

HTTP连接的处理

HTTP允许在客户端和最终的源端服务器之间存在一系列HTTP中间实体(代理,高速缓存等)。
Connection头部有一个由逗号分隔的连接标签列表,这些标签为此连接指定了不会传播到其他连接中去的选项。Connection头部及其标签指明的头部只能用于相邻的端点,转发时需要被移除。这种称为逐跳首部

连接分类:单个串行、并行(多条tcp连接)、持久连接(重用)、管道化连接(重用、并发)、复用连接



注意:并行不一定快,因为其会严重加重服务器或代理的压力。
浏览器通常会被并行数量作出限制(如同域名下最多4个)。

持久连接:重用已对目标服务器打开的空闲持久连接,这样可以避开缓慢的连接建立阶段,避开慢启动的拥塞适应阶段,以便更快速地进行数据的传输。
持久连接分类:HTTP1.0+keep-alive, HTTP1.1的默认persistent连接。

关于HTTP1.0Keep-Alive与Http1.1持久连接,请参考我另一篇文章。
HTTP1.0Keep-Alive:HTTP1.0默认不开启,需要请求和响应头都有Connection: Keep-Alive,否则连接就不持久。
Http1.1持久连接:不关心Connection: Keep-Alive,甚至需要废除这个。因为默认开启了持久连接,所以只需要在要关闭连接时发送Connection: close头就可以了。
HTTP1.0Keep-Alive与代理交互时可能出现的问题:Keep-Alive和哑代理,Connection头和盲中继,代理和逐跳首部、插入Proxy-Connection

管道化连接

基于Http1.1持久连接,可选地使用请求管道。

要求:请求顺序与响应顺序一致性。
限制:HTTP客户端不应该用管道化的方式发送会产生副作用的请求,一般指POST(非幂等方法).出错的时候,管道化方式会阻碍客户端了解服务器执行的是一系列管道化请求中的哪一些。由于无法安全地重试POS这样的非幂等请求,所以出错时,就存在某些方法永远不会被执行的风险。
这也就是为什么页面提交没反应过来,再次提交或者刷新时,大多数浏览器会提示你是否需要再次提交表单的原因。

关闭连接

1、任意关闭
2、Content-Length及截尾操作
3、连接关闭容限、重试、幂等性:
如果一个事务,不管是执行一次还是很多次,得到的结果都相同,这个事务就是幂等性。而POST是非幂等的。

4、正常关闭连接
我们知道TCP连接是双向的。有输入与输出队列,分别负责读与写。

完全关闭(socket.close)与半关闭(socket.shutdown):是否将输入输出通道都关闭,或者只关闭其中一个。
TCP关闭及重置错误:

关闭连接的输出信道总是很安全的。连接另一端的对等实体会在从其缓冲区中读出所有数据之后收到连接关闭的通知。
关闭连接的输入信道比较危险,除非你知道另一端不打算发送其他数据了。否则操作系统会向另一端的机器回送一条TCP连接被对端重置的报文,同时删除对端还未读取的所有缓存数据(尽管其中的大部分都已抵达你的机器了)。对于管道化连接来说,这是非常糟糕的情形。

正常关闭流程:先关闭输出通道-等待对端关闭它的输出信道-两端都告诉了对方不会再发送任何数据(即关闭了输出信道,并且关闭通知被对端读取了)-双方完全关闭连接。这样就不会有重置及清空缓冲数据的危险。
实际中,由于网络的不可靠,后面的步骤不一定能正常走完,所以一般需要周期性检查,超过一定时间仍不行就执行强制关闭,以节约资源。

原文:大专栏  HTTP权威指南笔记之连接管理


原文地址:https://www.cnblogs.com/chinatrump/p/11607255.html