背景: 随着业务增长,API 接口 499,502 的数量也呈现增长的趋势。当下使用的 nginx + gunicorn + flask 的框架结构看起来已经到达了瓶颈。
故探索使用 nginx + uwsgi + flask 的架构来提示性能。
名词解释:gunicorn/uwsgi都是wsgi协议(python web server gateway interface)的实现,
它们做的事情是协议转换,协议的一头是web app(如flask, django等framework的app),
另一头是web server(如apache, nginx等),gunicore/uwsgi在默认的情况下都是同步模型,
但都比通常的web framework实现得好。
使用的版本:
gunicorn==20.0.4
uWSGI==2.0.20
测试服务器信息:
CPU&内存
8核16 GiB
操作系统
Ubuntu 16.04 64位更换操作系统
实例规格
ecs.t5-c1m2.2xlarge(性能约束实例)升降配
实例规格族
ecs.t5
开始测试:
uwsgi 配置详情
# http 方式
[uwsgi]
http = :5001
chdir = /home/www/xxx/xxx/
wsgi-file = scripts/web.py
callable = app
processes = 1,4,8,16
threads = 1
# socket 方式
[uwsgi]
module = scripts.web:app
master = true
processes = 1,4,8,16
socket = hebe.sock
chmod-socket = 660
vacuum = true
die-on-term = true
gunicorn 配置详情
./ve gunicorn --preload \
--backlog 2 \
-w 1, 4, 8, 16 \
-b 10.111.1.180:8011 \
-b 127.0.0.1:8011 \
-c /home/www/xxx/xxx/scripts/gunicorn_config.py \
scripts.server:app
测试工具 wrk
`[root@jerrik /]# wrk -t12 -c100 -d30s [http://www.baidu.com ](http://www.baidu.com%20/)`
`Running 30s test @ [http://www.baidu.com](http://www.baidu.com/)`
`12 threads and 100 connections`
`Thread Stats Avg Stdev Max +/- Stdev`
`Latency 211.76ms 304.92ms 1.97s 88.17%`
`Req/Sec 72.93 68.72 797.00 90.97%`
`23725 requests in 30.05s, 347.47MB read`
`Socket errors: connect 0, read 48, write 0, timeout 50`
`Requests/sec: 789.57`
`Transfer/sec: 11.56MB`
解释说明
- 12 threads and 100 connections:
- 总共是12个线程,100个连接(不是一个线程对应一个连接)
- latency和Req/Sec:
- 代表单个线程的统计数据,latency代表延迟时间,Req/Sec代表单个线程每秒完成的请求数,他们都具有平均值, 标准偏差, 最大值, 正负一个标准差占比。一般我们来说我们主要关注平均值和最大值. 标准差如果太大说明样本本身离散程度比较高. 有可能系统性能波动很大.
- 23725 requests in 30.05s, 347.47MB read
- 在30秒之内总共有23725个请求,总共读取347.47MB的数据
- Socket errors: connect 0, read 48, write 0, timeout 50
- 总共有48个读错误,50个超时.
- Requests/sec和Transfer/sec
- 所有线程平均每秒钟完成了789.57个请求,每秒钟读取11.56MB数据量
CPU 密集型测试:
@app.route('/py.cpu')
def python_cpu():
"""
测试 cpu 性能
:return:
"""
def double_fact(x):
ans = 1
for i in range(1, x + 1):
if i % 2 == x % 2:
ans *= i
return ans
def asin(x, t):
answer = 0
for k in range(0, t + 1):
a = (double_fact(2 * k - 1) / double_fact(2 * k)) * (pow(x, 2 * k + 1) / (2 * k + 1))
print("k=%d,a=%s" % (k, a))
answer += a
return answer
app.logger.info(asin(1, 100) * 2)
return success(dict(version=sys.version))
IO 密集型测试:
@app.route("/py.io")
def python_io():
"""
测试网络 io 性能
:return:
"""
import requests
url = "https://www.idejian.com/"
requests.get(url) # 发get请求
app.logger.info(sys.version)
return success(dict(version=sys.version))
结果比较: ./wrk -t 16 -c 100 -d 10 https://xxxx.com/py.cpu
CPU 密集型API
结果比较: ./wrk -t 16 -c 100 -d 10 https://xxxx.com/py.io
IO 密集型API
结论:同步调用下,uwsgi 并没有比 gunicorn 更高性能。和预期不符,不知道是否是测量的方式有问题。 暂时不考虑使用 uwsgi ,继续优化 gunicorn
gunicorn 优化项目
- 调整进程数据为 cpu *2 + 1
- gunicorn worker_class = "eventlet"
- gunicorn preload 关掉
- gunicorn backlog 调整为 2048
在测试环境中测下来,上面的四项调整有明显的性能提升,但也带来了新的问题。
- 异步的为每个连接创建一个“绿色”线程,在处理IO机密型程序时,有明显的性能提升。
- 弊端是为每个连接创建一个“绿色”线程,在高峰期的时候,mysql 等其他有连接池的工具,不能提供足够的链接。导致程序报错。