连接管理
连接管理
4.1 TCP连接
世界上几乎所有的HTTP通信都是由TCP/IP承载的,TCP/IP是全球计算机以及网络设备都在使用的一种常见的分组交换网络分层协议集。
比如,你想要获取joe的五金商店最新的电动工具价目表:
http://www.joes-hardward.com:80/power-tools.html
浏览器收到这个URL,就会执行以下步骤:
将服务器IP地址和端口号从URL中分离出来
建立到Web服务器的TCP链接
通过这条连接发送一条请求报文
读取响应
关闭连接
4.1.1 TCP的可靠数据和管道
HTTP连接实际上就是TCP连接和一些使用连接的规则。TCP连接是因特网上的可靠连接。
TCP为HTTP提供了一条可靠的比特传输管道,从TCP连接一段填入的字节会从另一端以原有的顺序、正确的传送出来
4.1.2 TCP流是分段的、由IP分组传送
TCP的数据是通过名为IP分组的小数据块来发送的。
这样的话HTTP就是“HTTP over TCP over IP” 这个”协议栈“中的最顶层。其安全版本HTTPS就是在HTTP和TCP之间插入了一个(称为TLS或SSL)密码加密层。
HTTP要传送一条报文时,会以流的形式将报文数据的内容通过一条打开的TCP连接按序传输。TCP收到数据流之后,会将数据流砍成端的小数据块,并将段封装在IP分组中通过因特网进行传输。所有这些工作都是由TCP/IP软件来处理的,HTTP程序员什么都看不到。
每个TCP段都是由IP分组承载,从一个IP地址发送到另一个IP地址的。每个IP分组包括:
1、一个UP分组首部(通常20字节)
2、一个TCP短首部(通常为20字节)
3、一个TCP数据块(0或多个字节)
IP首部包含了源和目的IP地址、长度和其他一些标记。TCP段的首部包含了TCP端口号、TCP控制标记、以及用于数据排序和完整性检查的一些数字值。
4.1.3 保持TCP连接的正确运行
在任意时刻计算机都可以有几条TCP连接处于打开状态。TCP是通过端口号来保持这些连接的正确运行的。
端口号和雇员使用的电话分机号很类似。就像公司的总机号码能将你接到前台,而分机号可以将你接到正确的雇员位置一样,IP地址可以将你连接到正确的计算机,而端口号则可以将你连接到正确的应用程序上去。TCP连接是通过4个值来辨识的:
<源IP地址、源端口号、目的IP地址、目的端口号>
这4个值一起唯一地定义了一条连接。两条不同的TCP链接不能拥有4个完全相同的地址组件(但不同链接的部分组件可以有相同的值)
4.1.4 用TCP套接字编程
操作系统提供了一些操纵其TCP连接的工具。为了更具体的说明问题,我们看一个TCP编程接口。这个套接字API向HTTP程序员隐藏了TCP和IP的所有细节。套接字API最初是为Unix操作系统开发的,但现在几乎所有的操作系统和语言中都有其变体存在。
s = socket(<parameters>) 创建一个新的、未命名、为关联的套接字
bind(s, <local IP:port>) 向套接字赋一个本地端口和接口
connect(s, <remote IP:port>) 创建一条连接本地套接字和远端主机以及端口的连接
listen(s,...) 标识一个本地套接字,使其可以合法接受连接
s2 = accept(s) 等待某人建立一条到本地端口的连接
n = read(s, buffer, n) 尝试从套接字向缓冲区读取n个字节
n = write(s, buffer, b) 尝试从缓冲区向套接字写入n个字节
close(s) 完全关闭套接字
shutdown(s,<side>) 之关闭TCP连接的输入或者输出端
getsockopt(s,...) 读取某个内部套接字配置选项的值
setsockopt(s,...) 修改某个内部套接字配置选项的值
套接字API允许用户创建TCP的端点数据结构,将这些端点与远程服务器的TCP端点进行连接,并对数据流进行读写。TCP API隐藏了所有底层网络协议的握手细节,以及TCP数据流与IP分组之间的分段和重装细节。
4.2 对TCP性能的考虑
4.2.1 HTTP事务的时延
建立TCP连接以及传输请求和响应报文的时间相比,事务处理时间可能是很短的。除非客户端或服务器超载,或正在处理复杂的动态资源,否则HTTP时延就是由TCP网络时延造成的。
HTTP事务的时延主要一下几种原因:
1、客户端根据URI确定Web服务器的IP地址和端口号。如果附近没有对URI中的主机进行访问,通过DNS解析系统将URI中的主机名转换为一个IP地址可能花费数十秒的时间。
2、接下来客户端会向服务器发送一条TCP连接请求,并等待服务器回送一个请求接受应答。每条新的TCP连接都会有连接建立时延。这个值通常最多只有一两秒钟,但如果有数百个HTTP事务的话,这个值会很快地地接上去
3、因特网传输请求报文,服务器处理请求报文都需要时间
4、然后web服务器会回送HTTP响应,这也需要时间
4.2.2 性能聚焦区域
列出了一些对HTTP程序员产生影响的、最常见TCP相关时延,其中包括:
TCP连接建立握手
TCP慢启动拥塞控制
数据聚集的Nagle算法
用于捎带确认的TCP延迟算法
TIME_WAIT时延和端口耗尽
4.2.3 TCP连接的握手时延
建立一条新的TCP连接时,甚至是在发送任意数据之前,TCP软件之间会交换一系列的IP分组,对连接的有关参数进行沟通。如果连接只用来传送少量数据,这些交换过程就会严重降低HTTP的性能。
TCP连接握手需要经过以下几个步骤:
1、请求信的TCP连接时,客户端要向服务器发送一个小的TCP分组,这个分组中设置了一个特殊的SYN标记,说明这是一个连接请求。
2、如果服务器接受了连接,就会对一些连接参数进行计算,并向客户端回送一个TCP分组,这个分组中的SYN和ACK标记都被置位,说明连接请求已被接受
3、最后客户端向服务器回送一条确认信息,通知它连接已经成功建立。现代的TCP栈都允许客户端在这个确认分组中发送数据。
小的HTTP事务可能在TCP上花费50%,或更多的时间。HTTP可以通过重用现存连接,来减小这种TCP建立时延锁造成的影响的。
4.2.4 延迟确认
由于因特网自身我发确保可靠的分组传输(因特网路由器超负荷的话,可以随意丢弃分组),所以TCP事先了自己的确认机制来保存数据的成功传输。
每个TCP段都有一个序列号和数据完整性校验和。每个段的接受者收到完好的段时,都会向发送者回送小的确认分组。如果发送者没有在指定的窗口时间内收到确认信息,发送者就认为分组已被破坏或者损坏,并重发数据。
由于发送确认报文很小,所以TCP允许在发往相同方向的输出数据分组中对其进行“捎带”。TCP将返回的确认信息与输出的数据分组结合在一起,可以更有效的利用网络。为了增加确认报文找到桐乡传输数据分组的可能性,很多TCP栈都实现了一种“延迟确认”算法。延迟确认算法会在一个特定的窗口时间内将输出确定存放在缓冲区中,以寻找能够捎带它的输出数据分组。如果在那个时间段内没有输出数据,就将确定信息放在单独的分组中传送。
但是,HTTP具体双峰特征的请求——应答行为降低了捎带信息的可能。当希望有相当反方向回传分组的时候,偏偏没有那么多。通常延迟确认算法会引入想打大的时延。根据所使用操作系统的不同,可以调整或禁用延迟确认算法。
4.2.5 TCP慢启动
TCP数据传输的性能还去决议TCP连接的使用期(age)。TCP连接会随着时间进行自我“调谐”,期初会限制连接的最大速度,如果数据成功传输,会随着时间的推移提高传输的速度。这种调谐被称为TCP慢启动(slow start),用于防止因特网的突然过载和拥塞。
TCP慢启动限制了一个TCP断点在任意时刻可以传输的分组数。简单来说,没成功接收一个分组,发送端就有了发送另外两个分组的权限。如果某个HTTP事务有大量数据要发送,是不能一次将所有分组都发送出去的。必须发送一个分组,等待确认,然后可以发送两个分组,每个分组都必须被确认,这样就可以发送四个分组了,以此类推。这种方式呗称为“打开拥塞窗口”。由于存在这种拥塞控制特性,所以新连接的传输速度回比一些已经交换过一定量数据的、“已调谐”连接慢一些。由于已调谐连接要更快一些,所以HTTP中有一些可以重用的现存连接的工具。
4.2.6 Nagle算法与TCP_NODELAY
TCP有一个数据流接口,应用程序可以通过它将任意尺寸的数据放入TCP栈中——即使一次只放入一个字节也可以!但是,每个TCP段都至少装载了40个字节的标记和首部,所以如果TCP发送了大量包含少量数据的分组,网络的性能就会严重下降。
Nagle算法(根据其发明者John Nagle命名)试图在发送一个分组之前,将大量TCP数据绑定在一起,以提高网络效率。
Nagle算法鼓励发送全尺寸的段,只有当所有其他分组都被确认之后,Nagle算法才匀速发送非全尺寸的分组。如果其他分组仍然在传输过程中,那就将那部分数据缓存起来。只有当挂起分组被确认,或者缓存中积累了足够发送一个全尺寸分组的数据时,才会将缓存的数据发送出去
Nagle算法可能会引发几种HTTP性能问题。首先,小的HTTP报文可能无法填满一个分组,可能会因为等待那些永远不会到来的额外数据而产生时延。其次,Nagle算法与延迟确认之间的交互存在问题——Nagle算法会阻止数据的发送,知道有确认分组抵达为止,但确认分组自身会被延迟确认算法延迟100~200毫秒。
HTTP应用程序常常会在自己的栈中设置参数TCP_NODELAY,禁用Nagle算法,提高性能。如果要这么做的话,一定要确保会向TCP写入大块的数据,这样就不会产生一堆小分组了。
4.2.7 TIME_WAIT累积与端口耗尽
TIME_WAIT端口耗尽是很严重的心梗问题,会影响到性能基准,但在现实中相对较少出现。
当某个TCP端点关闭TCP连接时,会在内存中维护一个小的控制块,用来记录最近所关闭连接的IP和端口号。这类信息只会维持一小段时间,通常是所顾忌的最大分段使用期的两倍(称为2MSL,通常为2分钟)左右,以确保在这段时间内不会创建具有相同地址和端口号的新连接。实际上,这个算法可以防止在两分钟内创建、关闭并重新创建两个具有相同IP地址和端口号的连接。
现在高速路由器的使用,使得重复分组几乎不可能在连接关闭的几分钟之后,出现在服务器上。有些操作系统会将2MSL设置为一个较小的值,但超过此值时要特别小心。分组确实会被赋值,如果来自之前链接的复制分组插入了具有相同连接值的新TCP流,会破坏TCP数据。
2MSL的连接关闭延迟通常不是什么问题,但是在性能基准环境下就可能会成为一个问题。进行性能基准测试时,通常只有一台或几台用来产生流量的计算机连接到某个系统中去,这样就限制了连接到服务器的客户端IP地址数。而且服务器通常会在HTTP默认的TCP端口80上进行监听。用TIME_WAIT防止端口号重用时,这些情况也限制了可用的连接值组合。
在只有一个客户端和一台web服务器的异常情况下,构建一条TCP连接的4个值:
<source-IP-address, soucr-port, destination-IP-address, destination-port >
其中的三个都是固定的————只有源端口号可以随意改变:
<client-IP, source-port, server-IP, 80>
客户端每次连接到服务器上时,都会获得一个新的源端口,以实现连接的唯一性。但由于可用源端口数量有限(比如,60000个),而且在2MSL秒内连接是无法重用的,连接率就被限制在了60000/120 =500次/秒。如果再不断进行优化,并且服务器的连接率不高于500次/秒,就可确保不会遇到TIME_WAIT端口耗尽问题。要修正这个问题,可以增加客户端负载生成机器的数量或者确保客户端和服务器在循环使用几个虚拟IP以增加更多的连接组合。
即使没有遇到端口耗尽问题,也要特别小心有大量连接处于打开状态的情况,或为处于等待状态的连接分配了大量控制块的情况。有大量打开连接或控制块的情况下,有些操作系统的速度回严重减缓。
4.3 HTTP连接的处理
4.3.1 常被误解的Connection首部
HTTP允许在客户端和最终的源端服务器之间存在一串HTTP中间实体(代理、高速缓存等)。可以从客户端开始,逐渐得将HTTP报文经过这些中间设备转发到源端服务器上去(或者反向传输)。
在某些情况下,两个响铃的HTTP应用程序会为它们共享的连接应用一组选项。HTTP额Connection首部字段中有一个由逗号分隔的连接标签列表,这些标签为此连接指定了一些不会传播到其他连接中去的选项。比如,可以用Connection:close来说明发送完下一条报文之后必须关闭的连接。
Connection首部可以承载三种不同类型的标签:
1、HTTP首部字段名,列出了至于此连接有关的首部
2、任意标签值,用于描述此连接的非标准选项
值close,说明操作完成之后需关闭这条持久连接
如果连接标签红包含一个HTTP首部字段的名称,那么这个首部字段就包含了一些连接连接有关的信息,不能将其转发出去。在将报文转发出去之前,必须删除Connection首部所列出的所有首部字段。由于Connection首部可以防止五一中对本地首部的转发,因此将逐跳首部名放入Connection首部称为“对首部的保护”,例如:
HTTP/1.1 200 OK
Cache-control:max-age=3600
Connection: meter, close, bill_my_credit-card
Meter: max-uses=3, mnax-refuses=6, dont-report
首部说明:不应该转发Meter首部,要应用假想的bill-my-credit-card选项,且本次事务结束之后应关闭持久连接。
4.3.2 串行事务处理时延
如果只对连接进行简单的管理,TCP的性能时延可能会叠加起来。比如,假设有一个包含了3个嵌入图片。如果每个事务都需要一条心的连接,那么连接时延和慢启动就会叠加起来。
除了串行加载引入的实际时延之外,加载一副图片时,页面上其他地方都没有动静也会让人觉得速度很慢。用户更希望能够同时加载多幅图片。
串行加载的另一个缺点是,有些浏览器在对象加载完毕之前无法获知对象的尺寸,而且它们可能需要尺寸信息来决定将对象放在屏幕的什么位置上,所有在加载了足够的对象之前,无法再屏幕上显示任何内容。在这种情况下,可能李玉兰器串行装载对象的进度很重要,但对象面对的确是一个空白的屏幕,对装载的进度一无所知。
还有几种现存的和新兴的方法可以提高HTTP的连接性能。
并行连接
通过多条TCP连接发起并发的HTTP请求
持久连接
重用TCP连接,以消除连接及关闭时延
管道化连接
通过共享的TCP连接发起并发的HTTP请求
复用的连接
交替传送请求和响应报文(实验阶段)
4.4 并行连接
如上所述,浏览器可以先完整的请求原始的HTML页面,然后请求对第一个嵌入对象,然后请求第二个嵌入对象等,以这种简单的方式对每个嵌入式对象进行串行的护理。但这样实在是太慢了!
HTTP允许客户端打开多条连接,并行地执行了多个HTTP事务。
4.4.1 并行连接可能会提高页面的加载速度
包含嵌入对象的组合页面如果能(通过并行连接)客服单条连接的空间时间和带宽限制,记载速度也会有所提高。试验可以重叠起来,而且如果单条连接没有充分利用客户端的因特网带宽,可以将未用带宽分配来装载其他对象。
4.4.2 并行连接不一定更快
即使并行连接的速度可能会更快,但不移动总是更快。客户端的网络带宽不足时,大部分的时间可能都是用来传递数据的。在这种情况下,一个连接到速度较快的服务器的HTTP事务就会很容易地耗尽所有可用的Modem带宽。如果并行加载,每个对象都会去竞争这有限的带宽,每个对象都会以比较慢的速度按比例记载,这样带来的性能提升就会很小,甚至没什么提升。
而且打开大量连接会消耗很多内存资源,从而引发自身的性能问题。
实际上,浏览器确实使用了并行连接,但它们会将并行连接的总数限制为一个较小的值(通常是4个)。如无其可以随意关闭来自特定客户端的超量连接
4.4.3 并行连接可能会让人“感觉”更快一些
4.5 持久连接
web客户端经常会打开到同一个站点的连接。比如,一个Web页面上的大部分内嵌图片通常是来自同一个Web站点,而且相当一部分指向其他对象的超链接通常都指向同一个站点。因此,初始化了对某服务器HTTP请求的应用程序很可能会在不久的将来对那台服务器发起更多的请求(比如,在线获取图片)。这种性质被称为站点本地性(site locality)
因此,HTTP/1.1(以及HTTP/1.0 的各种增强版本),允许HTTP设备在事务处理结束之后将TCP连接保持在打开状态,以便于为未来的HTTP[请求重现现有的连接。在事务处理结束之后仍然保持在打开状态的TCP连接被称为持久连接。非持久连接会在每个事务处理结束之后关闭。持久连接会在不同事务之间保持打开状态,知道客户端或者服务器决定将其关闭为止。
重用已对目标服务器打开的空闲持久连接,就可以避开缓慢的连接建立阶段,而且,已经打开的连接还可以避免慢启动的拥塞适应阶段,以便更快速的数据的传输。
4.5.1 持久以及并行连接
我们看到,并行连接可以提高复合页面的传输速度,但并行连接也有一些缺点。
1、每个事务都会打开/关闭一条新的连接,会耗费时间和带宽
2、由于TCP慢启动特性的而存在,每条新连接的性能都会有所降低
3、可打开的并行连接数量上实际是有限的
持久连接有一些比并行连接更好的地方。持久连接降低了时延和连接建立的开销,将连接保持在已调谐状态,而减少了打开连接的潜在数量。但是,管理持久连接时要特别小心,不然就会累积出大量的空闲空间,耗费本地以及远程客户端和服务器上的资源
持久连接与并行连接配合使用可能是最高效的方式。
4.5.2 HTTP/1.0+ keep-alive连接
大约从1996年开始,很多HTTP/1.0浏览器和服务器都进行了扩展,以支持一种被称为keep-alive连接的早期实验性持久连接。
在串行连接上实现4个HTTP事务的时间线和一条持久连接上实现同样事务所需的时间线进行了比较,由于去除了进行连接和关闭连接的开销,所以时间线有所缩减。
4.5.3 Keep-Alive 操作
keep-alive已经不再使用了,而且在当前的HTTP/1.1规范中也没有对它的说明了,但浏览器与服务器对keep-alive握手的使用仍然相当广泛。
实现HTTP/1.0 keep-alive连接的客户端可以通过Connection: Keep-Alive首部请求将一条连接保持在打开状态。
如果服务器愿为下一条请求将保持打开状态,就在响应中包含相同的首部。如果响应中没有Connection:Keep-Alive首部,客户端就会认为服务器不支持keep-alive,会在发回响应报文之后关闭连接。
4.5.4 Keep-Alive 选项
注意,Keep-alive首部只是请求将连接保持在活跃状态。发出keep-alive请求之后,客户端和服务器并不一定会同意进行keep-alive会话它们可以在任意时刻关闭空闲的keep-alive连接,big可以随意限制keep-alive连接所处理事务的数量。
可以为Keep-Alive通用首部中指定的、由逗号分隔的选项来调节keep-alive的行为。
1、参数timeout是在Keep-Alive响应首部发送的。它估计了服务器希望将连接保持在活跃状态的时间。这并不是一个承诺值。
2、参数max是在Keep-Alive响应首部发送的。它估计了服务器还希望为多少事务保持次连接的活跃状态。这并不是一个承诺值。
3、Keep-Alive 首部还可以支持任意未经处理的属性,这些属性主要用于诊断和调试,语法为name [=value]
Keep-Alive首部是可选的,但只有在提供Connection:Keep-alive时才能使用它
4.5.5 Keep-Alive连接的限制和规则
使用keep-alive连接时有一些限制和一些需要澄清的地方。
1、在HTTP/1.0中,keep-alive并不是默认请求的。客户端必须发送一个Connection:Keep-Alive请求首部来激活keep-alive连接。
2、Connection: Keep-Alive 首部必须随着所希望保持持久连接的报文一起发送。如果客户端没有发送Connection:Keep-Alive首部,服务器就会在那条请求之后关闭连接。
3、客户端探明响应中没有Co0nnection:Keep-Alive响应首部,就可以知道服务器发出响应之后是否会关闭连接了。
4、只有在无需检查到连接的关闭即可确定报文实体主体部分长度的情况下,才能将连接保持在打开状态————也就是说实体的主体部分必须要有正确得到Content-Length,很多部件媒体类型,或者用分块传输编码的方式进行了编码。在一条keep-alive信道中回送错误的Content-Length是很糟糕的事,这样的话,事务处理的另一端就无法精确的检测出一条报文的结束和另一条报文的开始了。

浙公网安备 33010602011771号