20200903 -
本身对nginx的也不是很理解,但是为了达到目的也是来了兴趣,本次主要是为了达到高并发的效果。为了调整这一块也是花费了很大的功夫。这里主要记录一下具体的脉络,看看都用了哪一些思路。
虽然很多都没有什么作用,但也给我提出了一个难题,那就是说,怎么来定位性能瓶颈呢。
文章最后只是得到了一个大致的方向,但是对于具体的定位,还是没有定论。
本质上,起初并不是为了进行nginx的调优,所以才有了之前的一篇文章《高并发HTTP请求客户端 - python语言编写》,但是在编写了C程序之后,发现客户端的CPU占满了,而服务端的CPU还有很多空闲,所以问题转化为调试nginx的性能。前面采用的客户端时利用python进行编程,而在一次测试中,使用了一些压测工具,性能是python的10倍,占用CPU还特别少,那么最终就是要进行调试nginx的性能。
因为nginx服务端的东西并不是我起的,而且我也不知道他们是怎么配的,所以一开始我以为他们就是请求个静态网页,在网上找点教程肯定是能达到极限的,但是实际上他们nginx后面跑了一个自己写的tomcat的服务端,性能惨不忍睹。
在测试python的时候,因为单个脚本是异步单进程单线程编程,所有需要将其开启多个进行来实现,直接用shell脚本启动即可,本机测试时,40个核,40个机器。
工具 | 请求每秒(RPS) | CPU | 具体配置 |
---|---|---|---|
python异步编程 | 4W5+ | 100(40个核每个) | 1000个并发TCP |
wrk压测工具 | 4W5+ | 不到5% | 10个线程,1000个并发TCP |
在第二个工具测试时,完全不用去管CPU的问题,因为利用率极低。所以,从这个角度来看,问题出在客户端上,也就发现了他们给出的URL是去访问tomcat的客户端。但是这里并不想自己去写后端的业务,第一步就是先来测试静态文件的方法。
所以第一步是简单调整Nginx的配置,这部分配置参考文章[1]中的内容;但是没有关闭日志(这也为后续提供了一个隐患)其余的就是照搬nginx配置文件,以及sysctl的内容。那么此时,就是简单的利用nginx来返回一个静态文件。
工具 | 请求每秒(RPS) | CPU | 具体配置 |
---|---|---|---|
python异步编程 | 4W5+ | 100(40个核每个) | 1000个并发TCP |
wrk压测工具 | 40W+ | 不到15%(10个核每个) | 10个线程,1000个并发TCP |
在前面写过的那篇python客户端中提到,可以通过HTTP流水线的方式来提高性能,但是个人猜测Nginx这样的配置可能不支持这种方式。就通过流量来直接查看效果,看看是不是在http响应回来之前就已经发送了另外一个请求,实际上从流量上并没有这样的效果,这也应证了之前的一些帖子说,很多服务都没有实现http流水线。下面来说以下从流量中对应的参数内容。
1)python异步编程
在python异步编程中,因为文件描述符的限制,所以只能是加入一个类似信号量的东西,然后每个协程请求这部分内容。在流量中,这个数字就是对应的TCP连接个数。
2)wrk压测过程
./wrk -t 40 -c 1000 -d 20 http://xxx
上述参数中,c是指连接数,也就是TCP连接数,这个在流量中也是能对应的。
已经确定了要使用wrk来测试最大数量的性能是多少。
此时最高性能达到400K每秒请求数量,但是在这个数量之后就再也上不去了,找不到具体的原因;我都已经翻到了使用dpdk来进行底层补包引擎;但是这里也说不准就是哪部分的问题。
先来说明一下当前的一些系统参数。
1)CPU 客户端、服务端都没有满,而且利用率都不是很高,服务端稍高,但客户端20左右;
2)网卡25G,但实际只有3.5-4个G的流量,即使是因为这些都是小包的缘故,这个计算量也没办法匹配上
3)内存更不用说了,根本不是性能瓶颈
4)写文件问题?(此时写日志是开启的),但是此时通过atop并看不到写文件有什么问题,因为没有爆红。(其实这里是一个陷阱)
根据前面提到的现象,CPU没有占满,我武断的认为这是因为实际上发包过程的中断已经被占满了,也就是说,CPU没有占满就是因为他一直在等待着发包过程,也就是这是一个IO密集的程序,他一直在等待发送结束。
然后我就通过这个思路去寻找优化网卡的方法,因为这个网卡是一个25G的网卡,但是实际上交换机并达不到这种速率,所以可以当作是一个10G的网卡来说(这里说法不对,后续的测试中发现能够达到25G的速率,所以这里还是有瓶颈,可能就是网卡的缘故,但为什么后文的大包就没有问题?)。在这个思路情况下,找到了利用DPDK作为底层的捕包引擎。
github[2]是一个利用DPDK作为底层捕包引擎,同时带有一个小型的协议栈,并且编译了nginx的版本;github[3]是一个编译的dpdk-nginx,底层还需要另一个编译的DPDK做驱动。这两个从性能来看,都有不错的表现。但是,因为实际环境中,机房的网卡的问题,最终没有实现这种方案。
(但是,对于后续的内容中,如果还需要性能优化,那么肯定能再从这个角度来进行优化,因为性能瓶颈出现在了网卡上了 - 但是这里因为机器的限制,实际上是带宽的限制)
在上面的分析中,没有经过具体的理论计算,只是武断的认为这里是因为网卡的原因导致了后续无法性能提升。
(实际上,本次性能问题就是写文件的问题,没来一条日志都直接写入了文件,所以access.log的日志特别大)
虽然最后解决问题的时候,通过将nginx的访问日志给关闭,达到了1050K的效果,但是我不知道为什么是这个原因。
首先,前面提到使用atop的时候,虽然看到了硬盘在持续独写,但是没有说这个成为了瓶颈,因为没有看到硬盘busy特别高的情况;然后,再次寻找问题的时候就找不到相关的问题了。所以即使我一开始想到了是不是将日志关一下来看看,但是看到这个具体的数据的时候,我也觉得,不应该是这个问题。
当时也是看到了一篇文章[4],才想到要不试一试。然后关了之后,就达到了1050K的效果,此时流量能到大致9.2G的效果,算是到了大致10G流量的极限了。
如果基于这个事实来反推原理,那就是,因为本身服务器的硬盘性能也是非常好的,所以没有出现那种IO不够,然后阻塞的情况;但是,就是因为涉及了这个操作,而且从日志上来看,是每行日志就刷新的情况,那就是每次请求都要带有一次系统调用的过程,所以导致了这部分内容占用了系统时间。在关闭日志之后,通过这种方式在访问时,就把这个时间节省了,你可以想象每秒1050K的访问数,那么日志数也是这么多,也就是每个请求都要写这么一个东西,就导致大部分时间都在写这个东西。
虽然最后误打误撞到了最后的效果,效果非常好,能够达到了百万请求数。但是这个也让我思考,到底应该从什么地方来进行性能调优呢?大概去年的时候,也经历过这种情况,当时的目标是调试一个DPDK的程序,查看到底是什么地方导致丢包,但是最后也只能得到一个大概。
在文章[4]中,提到如果nginx的性能不高,通过查看CPU利用率来相关的诊断,对于我这种情况,那就是CPU利用率上不去,那么上不去就可以尝试解决网络IO或者文件IO,前面也提到,网卡实际速率跟最大速率差距有点大,最后还是定位了文件IO。从这个角度来看,那就是通过这些手段来解决。
前面说到的只是一种主观的推断,但是没有具体的证据是否是正确,或者实际的数据来进行验证。这个才是真正要关键解决的问题。
前面的测试中,只是利用了一个最小的网页,就直接使用的nginx的默认网页(0.6K),此时的性能大致能到1050K,但是在测试了大文件之后,比如500K的文件,这个数值就直接下降了非常多这种情况就比较麻烦了,从流量上来看,居然能够到达24G,也就是说,我前面的推论是不正确的,我前面以为,虽然是25G网卡,但是网线和交换机都支持不到这个速率,但实际情况是居然能到。
那么,从这个角度来看,那此时应该就是发送的小包太多的原因了,不过也有待考证。这里的问题就是,为什么发送小包的情况下,网卡速率上不去了呢?可能的原因就是PPS的问题,实际上在测试大文件的时候,虽然网卡流量速率上去了,但是他的PPS稳定在250W左右,而在小包的情况下,收发总计的PPS也是200W左右,也就是说,但25G的网卡,如果算是每个帧都是64字节,那大致上就是488W最大速率。所以很有可能是因为网卡中断部分已经上不去了,但是实际上还是那个问题,CPU的利用率并不是很高。这也是一个比较关键的问题。
在前文中,在关闭了日志之后,就可以达到每秒100W的请求数,但是这种方法的弊端是,只能在测试工具上查看这个结果;如果想在nginx中查看,就必须是开启日志,这其实挺矛盾的。但是我记得是有相关的工具可以统计的,例如nginx自己的模块。
文章[5]中第一个方法就是开启nginx的一个模块stub_status,然后每次测试的时候重启nginx,测试完成查看http://1.1.1.1/nginx_status即可。
[1] 500,000 Requests/Sec – Modern HTTP Servers Are Fast
[2]F-Stack/f-stack
[3]ansyun/dpdk-nginx
[4]nginx cpu过高或过低–状态简单分析与监控
[5]Monitoring NGINX