18910140161

分析影响http性能的常见因素

顺晟科技

2021-06-16 10:17:03

288

我们在这里讨论HTTP性能是基于最简单的模型,即单个服务器的HTTP性能。当然,它也适用于大规模负载平衡集群。毕竟,这种集群也是由多个服务器组成。此外,我们还排除了客户端或服务器本身过载或者HTTP协议实现的软件使用不同的IO模型。另外,我们也忽略了DNS解析过程和web应用开发本身的缺陷。

从TCP/IP模型来看,HTTP的下层是TCP层,所以HTTP的性能很大程度上取决于TCP的性能。当然,如果是HTTPS的话,应该加TLS/SSL层,但是可以肯定的是,HTTPS的性能肯定比HTTP差,这里也没有提到它的通信过程。总之层数越多,性能损失越严重。

在上述条件下,影响HTTP性能的最常见因素包括:

TCP连接建立,即三次握手阶段

TCP慢启动

TCP延迟确认

Nagle算法

时间等待和端口耗尽的累积

服务器端口不足

服务器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将被断开,并且在TCP慢启动过程完成之前,TCP连接可能被断开。然后网页的其他后续资源会继续建立TCP连接,每个TCP连接都会有一个缓慢的启动阶段。这个性能可想而知,所以为了提高性能,可以打开HTTP的持久连接,也就是keepalive。

另外,我们知道TCP有窗口的概念,在发送方和接收方都有。一方面,窗口的功能保证了发送方和接收方在管理数据包时变得有序;此外,多个数据包可以有序发送,从而提高吞吐量;还有一点就是这个窗口的大小可以调整,防止发送方发送数据比接收方快。虽然窗口解决了通信双传输速率的问题,但是网络会经过其他网络设备。发送方如何知道路由器的接收能力?于是就有了上面介绍的拥塞控制。

TCP延迟确认

首先,我们需要知道什么是承认。意思是发送方向接收方发送一个TCP段,接收方发回一个确认,表示收到。如果发送方在一定时间内没有收到确认,则需要重新发送TCP段。

确认消息通常比较小,也就是一个IP包可以携带多个确认消息,所以为了避免发送过多的小消息,接收方在发回确认消息的时候会等着看是否还有其他数据发送给接收方。如果有,确认消息和数据将在一个TCP段中一起发送,如果在某个时间内没有其他数据要发送,将在单独的数据包中发送。其实这样做的目的是为了尽可能减轻网络负担。

一个流行的例子是物流。卡车的负载是确定的。如果是10吨,从A市到B市,你肯定希望能尽量装一车,而不是小包裹一来就起床开车去B市。

因此,TCP设计为数据包一到达就不返回ACK确认,但通常会在缓存中累积一段时间。如果有同方向的数据,会捎带之前的ack确认,但不能等太久,否则对方会认为包丢失,触发对方重传。

对于不同的操作系统,是否使用以及如何使用延迟确认会有所不同。比如Linux可以启用,也可以关闭,关闭就是一个接一个的确认,也是一种快速确认模式。

需要注意的是,是否启用或者设置多少毫秒取决于场景。比如网游场景,确认一定要尽快完成,SSH会话可以使用延迟确认。

对于HTTP,我们可以关闭或调整TCP延迟确认。

Nagle算法

其实这个算法也是为了提高IP包的利用率,减轻网络负担而设计的,还是涉及到小包和全包(按照1500字节的以太网标准MTU,所有小于1500的包都不是全包),但是再小的包也不会小于40字节,因为IP头和TCP头各占20字节。如果你发送一个50字节的数据包,实际上意味着有效数据太少。就像延迟确认一样,小数据包在局域网中不是大问题,主要影响广域网。

实际上,这个算法意味着,如果发送方在当前TCP连接中已经发送了消息,但是还没有收到确认,那么如果发送方还有小消息要发送,就不能发送,而应该放在缓冲区中等待之前发送的消息的确认。接收到确认后,发送方将在缓冲区中收集同方向的小消息,并将其组装成一条消息进行发送。事实上,这意味着接收方返回确认的速度越快,发送方发送数据的速度就越快。

现在来说说延迟确认和Nagle算法结合带来的问题。其实很容易看出,由于延迟确认,接收方会在一定时间内累积ACK确认,而发送方如果在这段时间内没有收到ACK,就不会继续发送剩余的非全尺寸数据包(数据分为多个IP包,发送方要发送的响应数据的包数不一定是1500的整数倍,所以数据末尾的一些数据很有可能是小尺寸IP包)。所以你在这里可以看到矛盾,所以这种问题会影响TCP传输中的传输性能,所以HTTP依赖于TCP,所以自然会影响HTTP的性能。通常我们在服务器端禁用这个算法,可以在操作系统上禁用,也可以在HTTP程序中设置TCP_NODELAY。比如可以在Nginx中使用tcp _ nodelay on禁用。

时间_等待累积和端口耗尽3

这是指TCP连接中的客户端或活动关机方。虽然服务器可以主动发起关机,但我们这里讨论的是HTTP性能。由于HTTP连接的特点,客户端通常会发起主动关机。

一个客户端发起一个HTTP请求(这里是对特定资源的请求,而不是打开所谓的主页,一个主页有n个以上的资源,会导致n个HTTP请求的发起)。在这个请求结束后,TCP连接将被断开,因此客户端上连接的TCP状态将出现一个名为TIME_WAIT的状态,这个状态通常需要2MSL4从这个状态到最终关闭。我们知道客户端访问服务器的HTTP服务会使用自己的随机高阶端口与服务器的80或443端口连接建立HTTP通信(其本质是TCP通信),这意味着客户端上可用的端口数量会被消耗掉。虽然客户端断开连接,但这个随机端口将被释放。在从TIME_WAIT到CLOSED的2MSL期间(如果客户端启动对同一服务器的HTTP访问),随机端口将不被使用,其目的之一是防止同一TCP套接字上的脏数据。由以上结论可知,如果客户端对服务器的HTTP访问过于密集,有可能是端口使用速度高于端口释放速度,最终导致由于没有可用的随机端口而无法建立连接。

如上所述,客户端通常会主动关闭连接。

TCP/IP详细卷1第2版P442最后一段写道,对于交互应用,客户端通常执行主动关机操作,进入TIME_WAIT状态,而服务器通常执行被动关机操作,不直接进入TIME_WAIT状态。

但是,如果web服务器打开并保持活动状态,当超时时间到达时,服务器将自动关闭。(这里我不是说TCP/IP详细说错了,而是说那一节主要是针对TCP的,没有介绍HTTP,而且说一般,不一定)

我用Nginx测试,在配置文件中设置keepalive_timeout 65s。Nginx默认设置为75s,设置为0表示keepalive禁用,如下图所示:

接下来我用Chrom浏览器默认访问Nginx提供的主页,通过抓包程序监控整个沟通过程,如下图:

从上图可以看出,有效数据传输后,中间出现标记为Keep-Alive的通信,65秒内无请求后服务器主动断开。在这种情况下,您将在Nginx的服务器上看到TIME_WAIT的状态。

服务器端口不足

有人说Nginx监控80或者443,客户端都是连到这个端口的。服务器端口怎么会用完?就像下图(忽略图中的TIME_WAIT,原因上面已经说了,因为Nginx的keepalive_timeout设置)

其实要看Nginx的工作模式。我们通常使用Nginx以代理模式工作,这意味着真正的资源或数据在后端Web应用程序上,如Tomcat。代理模式的特点是代理服务器代替用户在后端获取数据。此时Nginx相对于后端服务器是一个客户端,Nginx会使用随机端口向后端发送请求。然而,系统中可用的随机端口的范围是确定的。可以使用sysctl net . IP v4 . IP _ local _ port _ range命令查看服务器上的随机端口范围。

通过延迟确认,Nagle算法和Nginx作为后端客户端角色,以代理模式将后端与随机端口连接,这意味着服务器的端口耗尽风险存在。如果随机端口的释放速度慢于与后端建立连接的速度,就可能发生。不过一般不是这样,至少我在我们公司的Nginx没有发现这种现象。因为是静态资源都在CDN上;其次,大部分后端接口都是REST接口,提供用户认证或数据库操作。其实如果后端没有瓶颈的话,这些操作基本都很快。但话又说回来,如果后端出现瓶颈,并且扩展或改变架构的成本比较高,那么面对大量并发时应该做的就是限制电流,防止后端被扼杀。

服务器HTTP进程打开的文件数达到更大值

我们说HTTP通信依赖于TCP连接,一个TCP连接就是一个套接字。对于类似Unix的系统,打开一个套接字就意味着打开一个文件。如果有100个连接到服务器的请求,一旦连接成功建立,服务器将打开100个文件。但是Linux系统中一个进程可以打开的文件数量是有限的ulimit -f,所以如果这个数量设置的太小,也会影响HTTP连接。对于Nginx或其他以代理模式运行的HTTP程序,通常一个连接会打开两个套接字,占用两个文件(命中Nginx本地缓存或Nginx直接返回数据除外)。因此,对于代理服务器来说,这个过程可以打开的文件数量也应该设置得更大。

持久连接保持活动

首先,我们需要知道keepalive可以设置为两个级别,两个级别有不同的含义。TCP的keepalive是一种探测机制,比如心跳信息,我们常说的心跳信息是指对方还在线,这个心跳信息是间隔发送的,也就是说彼此之间的TCP连接应该一直是开放的;HTTP中的保活是一种重用TCP连接的机制,以避免频繁的TCP连接。所以一定要明白,TCP的保活和HTTP的保活不一样。

超文本传输协议的保活机制

非持久连接会在每次HTTP事务完成后断开TCP连接,在下一次HTTP事务中重新建立TCP连接,这显然不是一种高效的机制。因此,在HTTP/1.1和HTTP/1.0的增强版本中,允许HTTP在事务结束后保持TCP连接打开,以便后续的HTTP事务可以重用此连接,直到客户端或服务器主动关闭连接。持久连接减少了TCP连接建立的次数,更大限度的避免了TCP慢启动带来的流量限制。

相关教程:HTTP视频教程

再来看看这张图。图片中的keepalive_timeout 65s设置了打开http的keepalive特性,并将超时时间设置为65秒。其实还有一个更重要的选项:keepalive _ requests 100它指示同一TCP连接可以发起的更大HTTP请求数,默认值为100。

在HTTP/1.0中,默认情况下不使用保活。当一个客户端发送一个HTTP请求时,它必须携带连接:保活的头来尝试激活保活。如果服务器不支持它,它将不会被使用,并且所有请求将以正常方式发出。如果服务器支持,响应头还将包括connection3360 keep-alive的信息。

在HTTP/1.1中,默认使用Keep-alive,除非另外指定,否则所有连接都是持久的。如果要在事务结束后关闭连接,HTTP的响应头必须包含Connection: CLose头,否则连接会一直保持打开,但不能一直打开,空闲连接也必须关闭,就像上面Nginx的设置一样,空闲连接最多会保持65秒,超过之后服务器会主动断开连接。

传输控制协议的保活

Linux上没有统一的开关来开启或关闭TCP的Keepalive功能。请检查keepalive设置sysctl -a | grep tcp_keepalive。如果您没有修改它,它将显示在Centos系统上:

一个

2

net . IP v4 . TCP _ keepalive _ intvl=75 #两个探测器之间的直接间隔是多少秒

net . IP v4 . TCP _ keepalive _ projects=9 #探测频率

net . IP v4 . TCP _ keepalive _ time=7200 #指示探测的执行频率,以秒为单位,此处为2小时

根据默认设置,上面的总体意思是每2小时检测一次。如果次检测失败,75秒后会再次检测。如果失败9次,会主动断开。

如何在Nginx上打开TCP级别的Keepalive,Nginx中有一个语句叫做listen,是服务器部分用来设置Nginx监听哪个端口的语句。事实上,它后面还有其他参数用于设置套接字属性。请参见以下设置:

一个

2

#表示打开,TCP的keepalive参数使用系统默认值

listen 80 default _ server so _ keepalive=on;

#表示明确关闭了TCP的保持活动

listen 80 default _ server so _ keepalive=off;

#表示开,设置为每30分钟检测一次。检测间隔使用系统默认设置,总共检测10次。

# set将覆盖上述系统默认设置

listen 80 default _ server so _ keepalive=30m :10;

因此,是否在Nginx上设置这个so_keepalive取决于具体场景。不要把TCP的keepalive和HTTP的keepalive混为一谈,因为Nginx不开so_keepalive,也不影响你的HTTP请求使用keepalive特性。如果客户端和Nginx直接之间或者Nginx和后端服务器之间有负载均衡设备,响应和请求都会经过这个负载均衡设备,那么你要注意这个so_keepalive。例如,在LVS的直接路由模式中,它不受影响,因为响应没有通过

然而,如果是网络地址转换模式,LVS应该注意,因为LVS在一定时间内保持传输控制协议会话。如果时间小于后端返回数据的时间,LVS将在客户端接收数据之前断开连接。

TCP拥塞控制有一些算法,包括TCP慢启动、拥塞避免等

有些地方也叫IP碎片化,但都是一个意思。至于为什么分片会简单地受到数据链路层的限制,不同的数据链路有不同的MTU,以太网是1500字节,有些场景是1492字节;FDDI的MTU是另外一个尺寸。仅考虑IP层,更大IP数据包为65535字节

《HTTP权威指南》 P90页不是特别清楚。这种情况是相对于客户端或服务器而言的,因为很可能被误解。当然,这并不意味着服务器不会用完端口,所以我在这里添加了两项

最长时间不超过2分钟

相关文章
我们已经准备好了,你呢?
2024我们与您携手共赢,为您的企业形象保驾护航