调研 gunicorn && uwsgi 性能

背景: 随着业务增长,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

20211217182527.jpg

结果比较: ./wrk -t 16 -c 100 -d 10 https://xxxx.com/py.io

IO 密集型API

20211217182648.jpg

结论:同步调用下,uwsgi 并没有比 gunicorn 更高性能。和预期不符,不知道是否是测量的方式有问题。 暂时不考虑使用 uwsgi ,继续优化 gunicorn

gunicorn 优化项目

  • 调整进程数据为 cpu *2 + 1
  • gunicorn worker_class = "eventlet"
  • gunicorn preload 关掉
  • gunicorn backlog 调整为 2048
在测试环境中测下来,上面的四项调整有明显的性能提升,但也带来了新的问题。
  • 异步的为每个连接创建一个“绿色”线程,在处理IO机密型程序时,有明显的性能提升。
  • 弊端是为每个连接创建一个“绿色”线程,在高峰期的时候,mysql 等其他有连接池的工具,不能提供足够的链接。导致程序报错。

你可能感兴趣的:(调研 gunicorn && uwsgi 性能)