nginx作为常用的web代理服务器,某些场景下对于性能要求还是蛮高的,所以本片文章会基于操作系统调度以及网络通信两个角度来讨论一下Nginx性能的优化思路。
基于操作系统调度进行Nginx优化CPU工作方式对于用户进程,CPU会按照下面的规则完成工作调度:
每个用户进程被加载到内存中,就会采用CFS调度算法,即对于vruntime少的优先运行,得到CPU的运行时间片就会运行,时间一到就会被挂起。
进程优先级越高得到时间片的时间就越长。
进程实际运行时间不一定和时间片相等,当进程遇到IO阻塞时也会被挂起。
如下图,我们的用户进程nginx就会被放到csf_rq中,操作系统会等deadline进程(dl_rq中的进程)和实时进程(rt_rq中的进程)拿到时间片运行完之后,从cfs_rq中取出最左节点运行。
优化的整体方向基于CPU工作原理的优化,我们一般都从以下两个角度考虑:
尽可能让操作系统历用尽可能多的CPU资源
尽可能占用更多的CPU时间片、减少进程间切换。
策略1(提升CPU利用率)在多核CPU的情况下,我们可以设置多个nginx进程使其尽可能利用CPU资源。
首先我们可以使用vmstat,查看CPU整体资源使用情况。
查看第一个CPU的资源使用情况,每个1秒输出一次,共输出5次sar-P015
复制代码
略
对应参数含义:
rxpck/s每秒钟接受的数据包rxKB/S每秒钟接受的数据包大小,单位为KBrxcmp/s每秒钟接受的压缩数据包rxmcst/s每秒钟接收的多播数据包
如下所示通过netstat这条指令可以看到网口的收发情况
netstat-I
RX-ERR/TX-ERR基本为0,说明网络质量良好
KernelInterfacetableIfaceMTURX-OKRX-ERRRX-DRPRX-OVRTX-OKTX-ERRTX-DRPTX-OVRFlgens332101000BMRUlo6553616000001600000LRUvirbr00BMUTCP内核参数详解_syn_retries
表示应用程序进行connect()系统调用时,在对方不返回SYN+ACK的情况下(也就是超时的情况下),第一次发送之后,内核最多重试几次发送SYN包,并且决定了等待时间。
Linux上的默认值是_syn_retries=6,也就是说如果是本机主动发起连接(即主动开启TCP三次握手中的第一个SYN包),如果一直收不到对方返回SYN+ACK,那么应用程序最大的超时时间就是127秒。
我们可以通过以下指令看到,他的默认值为6
cat/proc/sys/net/ipv4/tcp_syn_retries6
对此我们可以实验以下印证这个问题,128服务器将127的包drop掉,看看127这台服务器的请求会不会重试6次
首先我们开启一个终端抓一次127服务器的发送给128的HTTP包
tcpdump-iens33-nn''
然后设置128的服务器将127服务器的包丢弃
这时候127使用curl尝试连通
curl
然后基于tcpdump进行监控:
tcpdump-iens33-nn''
实验结果最终为发现,确实超市重试了6次,每次超时等待时间都是上一次的2倍
tcpdump:verboseoutputsuppressed,use-vor-vvforfullprotocoldecodelisteningonens33,link-typeEN10MB(Ethernet),capturesize262144bytes19:35:02.372499:Flags[S],seq3711897357,win29200,options[mss1460,sackOK,TSval19661434ecr0,nop,wscale7],length0sysctl-p重试了两次20:33:44.043910:Flags[S],seq2527903951,win29200,options[mss1460,sackOK,TSval23183104ecr0,nop,wscale7],length020:33:46.046469:Flags[S],seq2527903951,win29200,options[mss1460,sackOK,TSval23185108ecr0,nop,wscale7],length0_synack_retries
当服务器接收到客户端发送的SYN连接请求报文后,回应SYNC+ACK报文,并等待客户端的ACK确认,如果超时会进行重传,重传次数由下列参数设置,默认为5
[root@localhost~]curl:(7):80;Connectiontimedout
抓到128服务器的包发现果然重试了5次
tcpdump-iens33-nn''tcpdump:verboseoutputsuppressed,use-vor-vvforfullprotocoldecodelisteningonens33,link-typeEN10MB(Ethernet),capturesize262144bytes20:47:42.062075:Flags[P.],seq3290041520:3290041568,ack2422181631,win4106,length4820:47:42.114303:Flags[.],ack49,win4106,length020:47:42.587274:Flags[P.],seq48:96,ack49,win4106,length48超时重试第1次20:47:43.595729:Flags[S.],seq806829342,ack2654770401,win28960,options[mss1460,sackOK,TSval23417006ecr24020034,nop,wscale7],length0超时重试第3次20:47:45.596646:Flags[S.],seq806829342,ack2654770401,win28960,options[mss1460,sackOK,TSval23419007ecr24020034,nop,wscale7],length0超时重试第5次20:47:49.998252:Flags[S.],seq806829342,ack2654770401,win28960,options[mss1460,sackOK,TSval23423408ecr24020034,nop,wscale7],length0
127作为nginx服务器,为了避免没必要的开销,无论与上游服务器还是和客户之前synack重试次数实际上也不要这么大,完完全全可以调小些例如2
_synack_retries=2
复制代码
_synack_retries=2
_syncookies该参数在Centos6、7已经默认为1,这个参数开启后不会将还为建立的tcp连接存到SYN队列中,而是响应一个ACK+算好的cookie值,只要对方发送SYN+ACK+cookie包后才会将其存到队列中,这样的设置可以避免syn洪泛攻击
[root@localhost~]cat/proc/sys/net/ipv4/tcp_syncookies1_max_backlog_max_backlog参数表示网卡接受数据包的队列最大长度,在阿里云服务器上,默认值是1000,可以适当调整。
_max_syn_backlog_max_syn_backlog参数决定已经收到syn包,但是还没有来得及确认的连接队列,这是传输层的队列,在高并发的情况下,必须调整该值,提高承载能力。
参数决定了端口监听队列的最大长度,存放的是已经处于ESTABLISHED而没有被用户程序(例如nginx)接管的TCP连接,默认是128,对于高并发的,或者瞬发大量连接,必须调高该值,否则会直接丢弃连接。
上述三个队列位置图解可以看到在高并发的情况下这些参数都必须调大。我们建议的配置如下,可以看到设置为65535因为操作系统内核允许完全建立的连接大小不能超过65535,而另外两个参数可以24800是因为这些连接是尚未建立连接的数据包,所以数量可以多一些
_max_backlog=24800_max_syn_backlog=24800=65535这时候我们就可以调整nginx的backlog,可以看到笔者设置为24800,虽然这个值已经大于操作系统的somaxconn大小,但这并不会报错,相反这样的设置可以使得nginx尽可能的榨取队列资源。
listen80deferredbacklog=24800;TCP的FastOpen该参数是用来加速TCP连接数据交互的TCP扩展协议,是Google在2011年论文提出的。
原有tcp协议三次握手以及数据交互如下图,必须完全建立连接后才能发送数据而经过RFC优化之后的的TCP交互如下图,可以看到在最后一次确认的时候可以直接将请求数据携带过去
而TCPFastOpen在此基础上,新增一个特性,在建立TCP连接期间,服务端会发给用户一个cookie,当本次TCP连接断开后,用户可以使用这个cookie快速与服务端建立连接。这个机制很适合用于nginx服务器与上游服务器的交互。
使用sysctl-a|greptcp_fast即可看到这个值的默认值为0代表不开启TFO,1代表作为客户端时拥有TFO机制,2代表作为服务端才拥有TFO机制,3代表该服务器完全支持TFO机制。
[root@localhost~]#sysctl-a|greptcp__fastopen=0_fastopen_key=0000-0000所以我们建议nginx作为服务器的情况下可以开启TFO,参数可以设置为
_fastopen=2总结文章是基于原有个人知识基础上,对旧知识进行巩固,以及新知识实践学习。对于基于操作系统的优化亦或者其他知识,我们在学习过程中势必会遇到很多陌生的概念,我们切不可急躁,善于使用搜索引擎以及原有积累的知识去理解每一个概念,将参考文献补充完善,以便于后续回顾以及后续遇到相同场景我们可以快速完成问题的解决。这就是学习的技巧——突破学习材料瓶颈,打破学习材料的局限性,完成技术栈的学习。