记一次CentOS+Docker+Nginx+uWSGI+MongoDB的单机并发量提升过程

在这里插入图片描述

无人机拍的广州南站


最近在做小程序后端开发,使用Docker+Nginx+uWSGI+MongoDB的架构
最终部署图如下:

在这里插入图片描述

最开始在对新增订单的接口进行压测时出现100并发就有大量失败的情况,日志显示"WriteConflict error: this operation conflicted with another operation.",分析后得出找出原因:
新增订单压测时是对单一用户进行,即单一用户新增100个订单,而每个新增订单的业务逻辑需要在事务中读写该用户的单个document,从而导致大量请求获取该document的写锁超时。由于业务逻辑没有问题,故将代码临时进行修改,使其压测时对不同的document进行读写,模拟出100个用户新增订单的场景。问题解决。

随后,又对该接口进行200、300、400的压测,均没有出现失败,但到500的时候又出现失败
在这里插入图片描述

MongoDB

查看日志后发现依旧是"WriteConflict error: this operation conflicted with another operation." 错误。经过一番搜索,通过提高事务锁最大等待时间可以解决问题

>>> db.adminCommand( { setParameter: 1, maxTransactionLockRequestTimeoutMillis: 3000 } );

该命令会将默认的等待5ms改为3000ms。如果你的mongodb是副本集,则需要在每个节点都执行一次上面的命令。
参考:https://segmentfault.com/a/1190000022518374

然后继续进行测试,500、600都没问题,700出现错误。
在这里插入图片描述
这时日志中没有出现"WriteConflict error: this operation conflicted with another operation." 错误。而是变为"SSL read failed (5) - closing connection",也就是说请求就没有到达Flask。这个问题原因就比较多了,可能是服务器的CentOS对请求进行了限流,也可能是容器中的Ubuntu,也可能是Nginx,也可能是uWSGI。所以要逐个进行排查。

uWSGI

uWSGI配置文件中对listen值进行修改

...

processes = 4
threads = 2
enable-threads=true
# 默认队列长度为100
listen = 2000

...

重启uWSGI后发现报错

*** Starting uWSGI 2.0.19.1 (64bit) on [Wed Jun  9 09:05:49 2021] ***
compiled with version: 9.3.0 on 07 June 2021 02:10:09
os: Linux-3.10.0-1160.24.1.el7.x86_64 #1 SMP Thu Apr 8 19:51:47 UTC 2021
nodename: c6232ed6e31d
machine: x86_64
clock source: unix
detected number of CPU cores: 8
current working directory: /app
detected binary path: /usr/local/bin/uwsgi
!!! no internal routing support, rebuild with pcre support !!!
uWSGI running as root, you can use --uid/--gid/--chroot options
*** WARNING: you are running uWSGI as root !!! (use the --uid flag) ***
chdir() to /app
your memory page size is 4096 bytes
detected max file descriptor number: 1048576
lock engine: pthread robust mutexes
thunder lock: disabled (you can enable it with --thunder-lock)
Listen queue size is greater than the system max net.core.somaxconn (500).

这里的意思是操作系统中的一个参数小于这个listen值,对于这个值的修改后面再进行说明

Nginx

主要是最大连接数worker_processes*worker_connections

worker_processes  auto; #nginx 进程数,建议按照cpu 数目来指定,一般为它的倍数
worker_rlimit_nofile 20000; #一个nginx 进程打开的最多文件描述符数目,理论值应该是最多打开文件数(ulimit -n)与nginx 进程数相除,但是nginx 分配请求并不是那么均匀,所以最好与ulimit -n 的值保持一致

events {
    worker_connections 2000; #每个进程允许的最多连接数, 理论上每台nginx 服务器的最大连接数为worker_processes*worker_connections
    multi_accept on;
}

http {
    keepalive_timeout 0;
}

Docker容器

version: "3.8"
services: 
    wxapp_api:
		...
        privileged: true
        sysctls:
            net.core.somaxconn: '2000'
        ...

进入容器中查看一下ulimit值,有的操作系统默认的ulimit值仅为几百(如Mac为256),如果过低则进行修改

# 查看
>>> ulimit -n
# 修改
>>> ulimit -n 100000

宿主CentOS

# 查看
>>> ulimit -n
# 修改
>>> ulimit -n 100000
# 临时生效
>>> sysctl -w net.core.somaxconn=4000
# 永久生效
>>> vim /etc/sysctl.conf
net.core.somaxconn= 4000
>>> sysctl -p

在做完上面所有工作后发现700的时候依旧存在"SSL read failed (5) - closing connection"
关键步骤来了

# TCP连接立即回收、回用(recycle、reuse)
>>> echo 1 > /proc/sys/net/ipv4/tcp_tw_reuse
>>> echo 1 > /proc/sys/net/ipv4/tcp_tw_recycle
# 不做TCP洪水抵御
>>> echo 0 > /proc/sys/net/ipv4/tcp_syncookies
>>> sysctl -p

在做完上面所有的设置后,800并发没有报错,900的时候出现少量错误,而且是"SSL read failed (5) - closing connection"错误,故还需要进一步分析。
在这里插入图片描述
我将请求方式改为GET,即请求到达Flask后,而不执行业务代码就直接返回,测试发现1000并发也没有问题,故问题出在业务代码部分。
在这里插入图片描述
由于800并发已经能满足需求了,就没有再继续深入探究。但明显感觉800并发并没有发挥出这套架构应有的性能。

参考:
https://segmentfault.com/a/1190000022518374
https://blog.csdn.net/apple9005/article/details/82556794


2021-06-17更新

在另外一个接口的压测过程中也出现800正常,900出现几十个异常的现象。
查看uWSGI日志后发现,出现异常的37个请求没有到达uWSGI,即问题还是出现在Nginx
在这里插入图片描述
再查看Nginx日志,发现
在这里插入图片描述
突然灵光一闪,瓶颈可能是我这个笔记本,毕竟需要一次产生900个请求,网卡、处理器、内存、操作系统都可能产生瓶颈。所以我换了一台服务器进行测试,发现1000并发也正常,所以基本可以确定之前出现问题的原因是测试机的瓶颈。
在这里插入图片描述


2021-06-26更新

>>> ab -c 1500 -n 1500 -T application/json  -p testing-body.txt  'https://xxxx.comactivity/v1/order/new'

上面测试的接口会向数据库中findOneAndUpdate一条数据
每次优化均在base的基础上进行,并清空数据库

次数base编译不输出日志添加索引编译&不输出日志&添加索引
第一次108.54108.42107.93115.65119.31
第二次92.0694.7792.35121.73118.08
第三次79.3479.9679.73119.35116.21
第四次66.6673.9870.56122.67115.66
第五次61.7767.3464.86113.80114.14
第六次58.2959.9159.96117.70116.69

参考:https://www.cloudbees.com/blog/getting-every-microsecond-out-of-uwsgi


版权声明:本文为MacwinWin原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。