将nginx +uwsgi+flask架构来搭建web服务。但是发现一个问题,如果http请求达到一定量后,nginx直接返回502。

大概知道问题应该在nginx和uwsgi上,限制了并发的处理数。

查了nginx uwsgi官方的资料。原来调优有好几个参数,并且系统本身也需要做一些调优

1.首先先看一下nginx.conf里影响处理的配置。

user nginx;
worker_processes xx; #可以设置成cpu个数,体验较佳的性能
error_log /var/log/nginx/error.log;
pid /var/run/nginx.pid;
worker_rlimit_nofile 65535; # 最大打开文件数,这个值需要<= worker_connections
events {
worker_connections 65535; # 最大连接数,这个值依赖系统的配置。
}

2.看一下系统的配置sysctl.conf

net.core.somaxconn = 2048 #定义了系统中每一个端口最大的监听队列的长度,这是个全局的参数。默认是128.优化可以根据系统配置做优化

3.uwsgi 的配置优化/etc/uwsgi.d/admin.ini

workers = 24 # 并发处理进程数

listen = 65535 # 并发的socket 连接数。默认为100。优化需要根据系统配置

在做优化之前,发现并发数过不了100.原因就是这里,uwsgi 的socket 默认链接为100.

做完调优,在此压测了一下性能,并发为10000时:

ab -r -n 100000 -c 10000 -H "User-Agent: python-keystoneclient" -H "Accept: application/json" -H "X-Auth-Token: 65e194" http://keystonehost:35357/v2.0/

压测报告:

Server Software: nginx/1.8.1
Server Hostname: keystonehost
Server Port: 35357
Document Path: /v2/
Document Length: 450 bytes
Concurrency Level: 15000
Time taken for tests: 30.136 seconds
Complete requests: 100000
Failed requests: 0
Write errors: 0
Total transferred: 72900000 bytes
HTML transferred: 45000000 bytes
Time per request: 4520.417 [ms] (mean)
Transfer rate: 2362.33 [Kbytes/sec] received

2016-08-10更新:

运行时可能产生504 Gateway Time out这样的错误,究其原因是因为相关参数设置的不当。

nginx和uwsgi整合时有三个参数可以用于设置超时时间,在nginx配置文件http->server->location中设置。

proxy_connect_timeout:默认60秒,与uwsgi-server连接的超时时间,该值不能超过75秒.若在超时时间内未能成功连接则断开连接尝试。

proxy_read_timeout:默认60秒,nginx等待uwsgi进程发送响应数据的超时时间。若有需要长时间运行才能产生输出结果的uwsgi进程则需将此参数调高。若在错误日志文件中看到 upstream timed out需将此参数调高。若超过超时时间还未收到响应则nginx关闭连接。

proxy_send_timeout:默认60秒,nginx向uwsgi进程发送请求的超时时间。超时时间由两次写操作的时间间隔算,而非整个请求。若超过超时时间仍没写入动作则nginx关闭连接。

出现504错误的接口主要是读取新闻内容进行解析,可能是正文过长导致读取数据超时,因此将proxy_read_timeout设为150,问题解决。

location /news/quality {
proxy_pass http://web1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_ignore_client_abort on;
proxy_read_timeout 150;
access_log /var/log/nginx/access.log main;
}

2016-08-29更新:

偶尔还是会出现502的error,查看了nginx的错误日志发现:

2016/08/29 01:00:51 [error] 9195#9195: *432169941 upstream prematurely closed connection while reading response header from upstream

查了资料后发现应该是socket 超时时间设置过短,默认是4s,uwsgi配置修改成10s后到目前为止正常。

http = 0.0.0.0:10053
socket-timeout=10

2016-12-05更新:

最近app搞活动访问量增加不少,又开始出现502 error了,增加了uwsgi的线程数threads = 20,消灭了其中一部分,但还是有很多访问频繁的服务会出现了502,开始从nginx配置查找原因。

发现长连接居然是打开的!如果你的服务器上请求量很大,那你最好还是关闭这个参数吧,这样很危险的。

现在的服务器CPU很强,所以不用考虑频繁的tcp连接对cpu造成的压力,故建议关闭你的长连接吧。

keepalive_timeout 0;

2017-3-21更新:

app投票活动导致访问量再次激增,app后端的限制做的薄弱,导致流量攻击的情况持续发生。我们数据部这边的日接口调用量已经超过了100w+,今天有几个接口频繁报错502和504. 根据前面几次的排查来看nginx和uwsgi的线程数应该是足够的,在uwsgi日志定位到错误log:

flask python 并发测试 flask设置并发数_Server

应该是listen 队列满的原因,主要原因是由于部分api (新闻推荐接口)处理慢,影响了内部云平台其他api的可用性。在并发较高的情况下就会出现该问题;直接后果就是其他api不能正常提供服务;

处理方法有以下3种:

(1)增加uwsgi listen 队列长度 :通过参数 --listen 1024 来提高监听长度;

(2)使用UNIX Domain Socket 来替代网络Socket ,它不需要经过网络协议栈,不需要打包拆包等操作,它只是将应用程序数据从一个进程cp到另一个进程,这正好适合nginx 和 uwsgi 在同一台机器的情况;通过 --socket /tmp/uwsgi.sock 来使用 Domain Socket;

(3)增加负载均衡,把压力均分到其他机器上;

第二种方法测试无法启用,第三种方法机器暂不够用,只能通过增加uwsgi listen 队列长度,添加--listen 2048依然不够,最后调整到10w,发现过一会还是会被阻塞。增加队列长度别忘了先修改系统参数,方法如下:

修改系统参数

这里直接修改配置文件了,重启后仍然有效。

修改/etc/sysctl.conf文件,添加或者修改这几个参数值

#对于一个经常处理新连接的高负载 web服务环境来说,默认的 128 太小了
net.core.somaxconn = 262144
#表示SYN队列的长度,默认为1024,加大队列长度为8192,可以容纳更多等待连接的网络连接数
net.ipv4.tcp_max_syn_backlog = 8192
#网卡设备将请求放入队列的长度
net.core.netdev_max_backlog = 65536

修改完成之后要记得

sysctl -p

重新加载参数

临时解决方案最终是在nginx为新闻推荐接口单独起了一个端口,这样至少在挂了的时候不会影响到其他接口正常使用,估计最终解决方案还是要增加均衡负载。