本站文章(技术文章和tank手记)均为社长矢量比特工作.实践.学习中的心得原创,请勿转载!

一次曲折的nginx+uwsgi问题排查、模型调优

web服务器 矢量比特

业务的系统模型是nginx+uwsgi,跑的python代码,相当于mongoDB的一层读api,负责读写mongo的数据,20台服务器每天的访问量在7000万左右,通过HTTP API的方式被其他业务系统调用,算下来量不是

       业务的系统模型是nginx+uwsgi,跑的python代码,相当于mongoDB的一层读api,负责读写mongo的数据,20台服务器每天的访问量在7000万左右,通过HTTP API的方式被其他业务系统调用,算下来量不是很大,不过业务本身是最核心的数据源,nginx层面做了一层uwsgi cache,命中率在30%左右。

       最近上了详细的日志分析质量监控后,发现了一些问题,其中一个问题是所有机房的响应时间会同一时刻突然抖动,平均响应时间从70ms抖动到5到10s,然后瞬间恢复,从状态码上看不出异常,具体表现如下:

整体抖动.png

        最初的判断是后端的mongo导致,从整个执行过程看,里面最重的逻辑就是数据库查询,而之前也出现过几次mongo的问题,导致整个服务不稳定,这次DBA的同学查询后,却没有找到慢查询。继续分析整个请求过程:域名解析——>网络建链——用户request——>nginx——>upstream——>6099端口到uwsgi——>python代码执行、mongo取数据——>数据吐出去后四次挥手。首先排除网络和域名解析的问题,因为如果基础服务出问题,会大量的业务报警,不会单单这个业务有问题。

       为了继续定位时间慢在了哪个环节,需要添加详细的时间日志,最好全流程监控,首先nginx层面在默认request_time的基础上添加了upstream_response_time、upstream_addr的字段,这样一来就能查出请求到了哪个upstream的uwsgi server,并能通过upstream_response_time侧面反映出uwsgi的执行时间,如果request_time远远大于upstream_response_time,就可以简单定位出nginx到用户端堵了,其实只要系统参数调好,像nginx这种异步费阻塞的模型很少堵的,到服务器上一看cpu、内存、网络、硬盘io利用率都不高,继续分析,最有可能的是后端uwsgi出问题了,其问题不是uwsgi本身那就是代码逻辑问题。继续找证据佐证,开发在python里也做了debug日志,截图如下:

python打出来取mongo的时间.png

        整理数据分析,目前情况是,个别request_time执行时间是60多s,mongo没有慢查询,python代码里打出来的时间是206ms,服务器没有遇到瓶颈,nginx到用户不会堵,从结果上看,是python代码没有及时的将执行结果吐给nginx。继续分析,问题出在nginx和uwsgi之间的衔接问题、或是uwsgi哪个地方有问题,遇到瓶颈,查找nginx的error日志,截屏如下:

error日志.png

        nginx与后端的uwsgi连接超时,也就是说请求根本没有发送过去,那为什么还会有执行200ok的结果呢,继续查加上日志字段后的日志时间情况,有意思的事情出现了:

访问日志.png        upstream_response_time和upstream_addr各打印了两条记录,豁然开朗,原来是连接第一个uwsgi在60s超时后,转去第二个uwsgi进行了请求,请求成功是9ms,所以最后总的请求时间是60.009ms,而且是200ok。

        问题出在uwsgi这一层可以确认了,那到底是什么问题导致请求超时呢,会是三次握手建链就没成功么,针对这个事儿,对6099端口的tcp链接信息做详细监控,过一段时间的绘制的图表如下:

业务端口tcp连接的详细信息.png     

         总的连接数非常低,而且没出现SYNRECV的状态,说明系统网络层面没有问题。连接数抖动的瞬间刚好是服务抖动的瞬间,这个也佐证了堵在uwsgi的结论,因为链接数的大小侧面反映了请求队列的情况,下一步就要去查uwsgi了,之前没有详细的去了解,与雄飞同学去看了uwsgi的监控方法,其中本地有uwsgitop的监控,通过http也有监控接口(后来发现http的监控接口有坑,加上zabbix后,服务器死了两次)。通过php-fpm去类别uwsgi,里面最重要的信息就是活跃进程数了,也就是当前干活的进程数量,通过下面uwsgitop的命令观察服务器一段时间发现如下:

uwsgitop /data1/comos/logs/uwsgi-stats.sock

uwsgitop.png

       出问题的瞬间,16个uwsgi进程全部busy,也就是说进程池跑满了,没有空余的进程处理新的请求,所以连接数会突增一下。解决的思路有两个,1个是扩大进程池,2个是去分析为什么代码执行不畅会把进程池跑满,其实第2个是本质原因,但是优化调整进程池是成本比较低的,马上找灰度机将原来16个进程效仿php-fpm调整到128个,配置文件如下:

processes = 128

调整后,与同环境的服务器数据对比如下:

优化前后数据对比.png

        抖动时波峰降低的很明显,说明此参数的调整起到了很好的作用,属于系统软件层面的调优。由于抖动还是存在,其业务依赖的某个上下游资源或者代码本身肯定还是有问题的,增加了进程数(服务器配置评估过没问题)相当于增加了单台服务器的处理能力,就是池子变大了,能够消化更多的请求,但如果代码或资源本身就有问题,不管怎么调大池子,还是都会有问题的。拿当前的业务看,一条穿透的请求大概在200ms,也就是一个进程1s最多处理5个请求,原来是16个进程,1s最多处理80个请求,其实已经在饱和边缘了,必须调整。


运维网咖社”原创作品,允许转载,转载时请务必以超链接形式标明文章 原始出处 、作者信息和本声明。否则将追究法律责任。http://www.net-add.com


©本站文章(技术文章和tank手记)均为社长"矢量比特"工作.实践.学习中的心得原创或手记,请勿转载!

喜欢 (37) or 分享 (0)
欢迎扫描关注微信公众号【运维网咖社
社长"矢量比特",曾就职中软、新浪,现任职小米,致力于DevOps运维体系的探索和运维技术的研究实践.