我们这里讨论HTTP性能是建立在一个最简单模型之上就是单台服务器的HTTP性能,当然对于大规模负载均衡集群也适用毕竟这种集群也是由多个HTTTP服务器的个体所组成。另外我们也排除客户端或者服务器本身负载过高或者HTTP协议实现的软件使用不同的IO模型,另外我们也忽略DNS解析过程和web应用程序开发本身的缺陷。
从TCP/IP模型来看,HTTP下层就是TCP层,所以HTTP的性能很大程度上取决于TCP性能,当然如果是HTTPS的就还要加上TLS/SSL层,不过可以肯定的是HTTPS性能肯定比HTTP要差,其通讯过程这里都不多说总之层数越多性能损耗就越严重。
在上述条件下,最常见的影响HTTP性能的包括:
TCP连接建立,也就是三次握手阶段
TCP慢启动
TCP延迟确认
Nagle算法
TIME_WAIT积累与端口耗尽
服务端端口耗尽
服务端HTTP进程打开文件数量达到最大
TCP连接建立
通常如果网络稳定TCP连接建立不会消耗很多时间而且也都会在合理耗时范围内完成三次握手过程,但是由于HTTP是无状态的属于短连接,一次HTTP会话接受后会断开TCP连接,一个网页通常有很多资源这就意味着会进行很多次HTTP会话,而相对于HTTP会话来说TCP三次握手建立连接就会显得太耗时了,当然可以通过重用现有连接来减少TCP连接建立次数。
TCP慢启动
TCP拥塞控制手段1,在TCP刚建立好之后的最初传输阶段会限制连接的最大传输速度,如果数据传输成功,后续会逐步提高传输速度,这就TCP慢启动。慢启动限制了某一时刻可以传输的IP分组2数量。那么为什么会有慢启动呢?主要是为了避免网络因为大规模的数据传输而瘫痪,在互联网上数据传输很重要的一个环节就是路由器,而路由器本身的速度并不快加之互联网上的很多流量都可能发送过来要求其进行路由转发,如果在某一时间内到达路由器的数据量远大于其发送的数量那么路由器在本地缓存耗尽的情况下就会丢弃数据包,这种丢弃行为被称作拥塞,一个路由器出现这种状况就会影响很多条链路,严重的会导致大面积瘫痪。所以TCP通信的任何一方需要进行拥塞控制,而慢启动就是拥塞控制的其中一种算法或者叫做机制。
你设想一种情况,我们知道TCP有重传机制,假设网络中的一个路由器因为拥塞而出现大面积丢包情况,作为数据的发送方其TCP协议栈肯定会检测到这种情况,那么它就会启动TCP重传机制,而且该路由器影响的发送方肯定不止你一个,那么大量的发送方TCP协议栈都开始了重传那这就等于在原本拥塞的网络上发送更多的数据包,这就等于火上浇油。
通过上面的描述得出即便是在正常的网络环境中,作为HTTP报文的发送方与每个请求建立TCP连接都会受到慢启动影响,那么又根据HTTP是短连接一次会话结束就会断开,可以想象客户端发起HTTP请求刚获取完web页面上的一个资源,HTTP就断开,有可能还没有经历完TCP慢启动过程这个TCP连接就断开了,那web页面其他的后续资源还要继续建立TCP连接,而每个TCP连接都会有慢启动阶段,这个性能可想而知,所以为了提升性能,我们可以开启HTTP的持久连接也就是后面要说的keepalive。
另外我们知道TCP中有窗口的概念,这个窗口在发送方和接收方都有,窗口的作用一方面保证了发送和接收方在管理分组时变得有序;另外在有序的基础上可以发送多个分组从而提高了吞吐量;还有一点就是这个窗口大小可以调整,其目的是避免发送方发送数据的速度比接收方接收的速度快。窗口虽然解决了通信双发的速率问题,可是网络中会经过其他网络设备,发送方怎么知道路由器的接收能力呢?所以就有了上面介绍的拥塞控制。
TCP延迟确认
首先要知道什么是确认,它的意思就是发送方给接收方发送一个TCP分段,接收方收到以后要回传一个确认表示收到,如果在一定时间内发送方没有收到该确认那么就需要重发该TCP分段。
确认报文通常都比较小,也就是一个IP分组可以承载多个确认报文,所以为了避免过多的发送小报文,那么接收方在回传确认报文的时候会等待看看有没有发给接收方的其他数据,如果有那么就把确认报文和数据一起放在一个TCP分段中发送过去,如果在一定时间内容通常是100-200毫秒没有需要发送的其他数据那么就将该确认报文放在单独的分组中发送。其实这么做的目的也是为了尽可能降低网络负担。
一个通俗的例子就是物流,卡车的载重是一定的,假如是10吨载重,从A城市到B城市,你肯定希望它能尽可能的装满一车货而不是来了一个小包裹你就立刻起身开向B城市。
所以TCP被设计成不是来一个数据包就马上返回ACK确认,它通常都会在缓存中积攒一段时间,如果还有相同方向的数据就捎带把前面的ACK确认回传过去,但是也不能等的时间过长否则对方会认为丢包了从而引发对方的重传。
对于是否使用以及如何使用延迟确认不同操作系统会有不同,比如Linux可以启用也可以关闭,关闭就意味着来一个就确认一个,这也是快速确认模式。
需要注意的是是否启用或者说设置多少毫秒,也要看场景。比如在线游戏场景下肯定是尽快确认,SSH会话可以使用延迟确认。
对于HTTP来说我们可以关闭或者调整TCP延迟确认。
Nagle算法
这个算法其实也是为了提高IP分组利用率以及降低网络负担而设计,这里面依然涉及到小报文和全尺寸报文(按以太网的标准MTU1500字节一个的报文计算,小于1500的都算非全尺寸报文),但是无论小报文怎么小也不会小于40个字节,因为IP首部和TCP首部就各占用20个字节。如果你发送一个50个字节小报文,其实这就意味着有效数据太少,就像延迟确认一样小尺寸包在局域网问题不大,主要是影响广域网。
这个算法其实就是如果发送方当前TCP连接中有发出去但还没有收到确认的报文的时候,那么此时如果发送方还有小报文要发送的话就不能发送而是要放到缓冲区等待之前发出报文的确认,收到确认之后,发送方会收集缓存中同方向的小报文组装成一个报文进行发送。其实这也就意味着接收方返回ACK确认的速度越快,发送方发送数据也就越快。
现在我们说说延迟确认和Nagle算法结合将会带来的问题。其实很容易看出,因为有延迟确认,那么接收方则会在一段时间内积攒ACK确认,而发送方在这段时间内收不到ACK那么也就不会继续发送剩下的非全尺寸数据包(数据被分成多个IP分组,发送方要发送的响应数据的分组数量不可能一定是1500的整数倍,大概率会发生数据尾部的一些数据就是小尺寸IP分组),所以你就看出这里的矛盾所在,那么这种问题在TCP传输中会影响传输性能那么HTTP又依赖TCP所以自然也会影响HTTP性能,通常我们会在服务器端禁用该算法,我们可以在操作系统上禁用或者在HTTP程序中设置TCP_NODELAY来禁用该算法。比如在Nginx中你可以使用tcp_nodelayon;来禁用。
TIME_WAIT积累与端口耗尽3
这里指的是作为客户端的一方或者说是在TCP连接中主动关闭的一方,虽然服务器也可以主动发起关闭,但是我们这里讨论的是HTTP性能,由于HTTP这种连接的特性,通常都是客户端发起主动关闭,
客户端发起一个HTTP请求(这里说的是对一个特定资源的请求而不是打开一个所谓的主页,一个主页有N多资源所以会导致有N个HTTP请求的发起)这个请求结束后就会断开TCP连接,那么该连接在客户端上的TCP状态会出现一种叫做TIME_WAIT的状态,从这个状态到最终关闭通常会经过2MSL4的时长,我们知道客户端访问服务端的HTTP服务会使用自己本机随机高位端口来连接服务器的80或者443端口来建立HTTP通信(其本质就是TCP通信)这就意味着会消耗客户端上的可用端口数量,虽然客户端断开连接会释放这个随机端口,不过客户端主动断开连接后,TCP状态从TIME_WAIT到真正CLOSED之间的这2MSL时长内,该随机端口不会被使用(如果客户端又发起对相同服务器的HTTP访问),其目的之一是为了防止相同TCP套接字上的脏数据。通过上面的结论我们就知道如果客户端对服务器的HTTP访问过于密集那么就有可能出现端口使用速度高于端口释放速度最终导致因没有可用随机端口而无法建立连接。
上面我们说过通常都是客户端主动关闭连接,
TCP/IP详解卷1第二版,P442,最后的一段写到对于交互式应用程序而言,客户端通常执行主动关闭操作并进入TIME_WAIT状态,服务器通常执行被动关闭操作并且不会直接进入TIME_WAIT状态。
不过如果web服务器并且开启了keep-alive的话,当达到超时时长服务器也会主动关闭。(我这里并不是说TCP/IP详解错了,而是它在那一节主要是针对TCP来说,并没有引入HTTP,而且它说的是通常而不是一定)

本文转载自中文网

本文转载自中文网
如需转载,请注明文章出处和来源网址:http://www.divcss5.com/html/h55799.shtml