一次难忘的nginx性能调优经历

举报
费德勒 发表于 2017/03/14 18:03:42 2017/03/14
【摘要】 本测试搭建的模型为weighttp client--->nginx proxy--->nginx server。

1 32核1610单板测试结果:

紫色行为未开启“keepalive”最佳TPS配置
蓝色行为开启“keepalive”最佳TPS配置

在保持进程数比例proxy:server为2:1的情况下,nginx server个数增加,TPS呈现较好的线性提升,直到server个数超过7个,TPS趋于平稳,达到性能瓶颈。

2 原因分析
2.1 2-10-8*1 Perf热点函数抓取结果:osq_lock占比4.61%
perf record -C 20-29 –g
perf report -g --no-childr

2.2 2-16-8*1 Perf热点函数抓取结果:osq_lock占比28.84%
perf record -C 14-29 –g
perf report -g --no-children

2.3 2.3 2-22-8*1 Perf热点函数抓取结果:osq_lock占比38.34%
perf record -C 8-29 –g
perf report -g --no-children

2.4 “keepalive“参数含义:设置到upstream服务器的空闲keepalive连接的最大数量
keepalive参数的含义非常的奇特,请看nginx文档中的说明:
Syntax: keepalive connections;
Default: —
Context: upstream
Activates the cache for connections to upstream servers. 激活到upstream服务器的连接缓存。 The connections parameter sets the maximum number of idle keepalive connections to upstream servers that are preserved in the cache of each worker process. When this number is exceeded, the least recently used connections are closed. connections参数设置每个worker进程在缓冲中保持的到upstream服务器的空闲keepalive连接的最大数量.当这个数量被突破时,最近使用最少的连接将被关闭。 It should be particularly noted that the keepalive directive does not limit the total number of connections to upstream servers that an nginx worker process can open. The connections parameter should be set to a number small enough to let upstream servers process new incoming connections as well. 特别提醒:keepalive指令不会限制一个nginx worker进程到upstream服务器连接的总数量。connections参数应该设置为一个足够小的数字来让upstream服务器来处理新进来的连接。

2.5 设置到upstream服务器的空闲keepalive连接的最大数量为800后:2-22-8*1 Perf热点函数抓取结果:osq_lock占比61.10%

upstreammysvr{
       server127.0.0.1:81weight=10;
       server127.0.0.1:82weight=10;
       server127.0.0.1:83weight=10;
       server127.0.0.1:84weight=10;
       server127.0.0.1:85weight=10;
       server127.0.0.1:86weight=10;
       server127.0.0.1:87weight=10;
       server127.0.0.1:88weight=10;
        keepalive800;
       }
   server{
       listen      80;
       location/{
           proxy_passhttp://mysvr;
            proxy_http_version1.1;
           proxy_set_headerConnection"";
       }

2.6 osq_lock热点函数调用流程图

2.7 性能瓶颈原因分析
从4组Perf抓取的热点函数”osq_lock”占比数据可以看出,当nginx proxy在进程数超过16个后,osq-lock占比急剧上升,并且当全部使用长连接后osq_lock的占比进一步提升达到61%,差不多是之前的2倍,而此时的CPU使用率并不满,才86%。
从这些结果出发,我怀疑osq_lock的占比在核数提升后急剧提高是由于长连接引起的,为了验证我将长连接的“keepalive”设置为0,果然osq_lock就没有占比了,进一步验证将“keepalive”设置为20,osq_lock占比为29.91%,“keepalive”设置为40以上,则osq_lock占比约为61%,此时可以确认osq_lock占比急剧上升是由于设置长连接且核数超过16个后引起的。
为了挖掘深层原因,我查看了nginx官方文档,源码和linux内核源码,有如下发现。
在http1.1中,引入了一种新的特性,即pipeline。那么什么是pipeline呢?pipeline其实就是流水线作业,它可以看作为keepalive的一种升华,因为pipeline也是基于长连接的,目的就是利用一个连接做多次请求。如果客户端要提交多个请求,对于keepalive来说,那么第二个请求,必须要等到第一个请求的响应接收完全后,才能发起,这和TCP的停止等待协议是一样的,得到两个响应的时间至少为2*RTT。而对pipeline来说,客户端不必等到第一个请求处理完后,就可以马上发起第二个请求。得到两个响应的时间可能能够达到1*RTT。nginx是直接支持pipeline的,但是,nginx对pipeline中的多个请求的处理却不是并行的,依然是一个请求接一个请求的处理,只是在处理第一个请求的时候,客户端就可以发起第二个请求。这样,nginx利用pipeline减少了处理完一个请求后,等待第二个请求的请求头数据的时间。其实nginx的做法很简单,前面说到,nginx在读取数据时,会将读取的数据放到一个buffer里面,所以,如果nginx在处理完前一个请求后,如果发现buffer里面还有数据,就认为剩下的数据是下一个请求的开始,然后就接下来处理下一个请求,否则就设置keepalive。
如果使用长连接,就可以利用一个连接做多次请求,这些请求来自不同进程,在请求结束时,这些进程要写同一个log文件。Linux为了支持多进程访问共享资源引入了锁,而当进程数超过16个后,锁出现的情况大幅上升,CPU的性能很大一部分被锁给消耗,造成了进程增加,性能上不去的情况,特别的看下面3种情况,当nginx proxy的进程从16上升到22时,吞吐量基本没有提升,每个进程还都满负载,多余的CPU性能都被osq_lock给消耗了。

2.8 结论
nginx proxy在进程数超过16后的性能瓶颈在于nginx使用长连接后多进程同时写log文件和Linux为了支持多进程访问共享资源引入了锁(mutex_lock,spin_lock,osq_lock等)。
针对这个问题,现有解决办法就是不记录log,利用以下设置就可以突破写log带来的性能瓶颈:
access_log /dev/null; //将存储access_log的路径设置为“垃圾桶”
error_log /dev/null; //将存储error_log的路径设置为“垃圾桶”。
采用长连接并且不记录log后,最优配置“单client5核-单proxy17核-单server10核”下TPS达到了271944 req/s,且32核全部满负荷。

作者 | 钱溢

转载请注明出处:华为云博客 https://portal.hwclouds.com/blogs

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。