gunicorn 高性能wsgi服务器

参考https://zhuanlan.zhihu.com/p/102716258

1|0Gunicorn是什么


Gunicorn Green Unicorn 是一个 UNIX 下的 WSGI HTTP 服务器,它是一个 移植自 Ruby 的 Unicorn 项目的 pre-fork worker 模型。

Gunicorn启动项目之后一定会有一个主进程Master和一个或者多个工作进程。工作进程的数量可以指定。主进程是维护服务器的运行,工作进程是实际处理请求的进程。所有请求和响应均由 Worker 处理。
同步的 Worker 一次处理一个请求。

2|0gunicorn flask 压测对比


测试例子: demo.py

from flask import Flask app = Flask(__name__) @app.route('/') def index(): return 'hello world!' if __name__ == '__main__': app.run()

2|1Flask启动压测


直接运行demo.py,使用flask自带的WSGI

[jian@laptop practics]$ python demo.py * Serving Flask app "demo" (lazy loading) * Environment: production WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead. * Debug mode: off * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)

2.使用ab工具进行压测

[jian@laptop practics]$ ab -n 500 -c 500 http://localhost:5000/ This is ApacheBench, Version 2.3 <$Revision: 1843412 $> Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/ Licensed to The Apache Software Foundation, http://www.apache.org/ Benchmarking localhost (be patient) Completed 100 requests Completed 200 requests Completed 300 requests Completed 400 requests Completed 500 requests Finished 500 requests Server Software: Werkzeug/0.16.0 Server Hostname: localhost Server Port: 5000 Document Path: / Document Length: 12 bytes Concurrency Level: 500 Time taken for tests: 0.477 seconds Complete requests: 500 Failed requests: 0 Total transferred: 83000 bytes HTML transferred: 6000 bytes Requests per second: 1049.28 [#/sec] (mean) Time per request: 476.515 [ms] (mean) Time per request: 0.953 [ms] (mean, across all concurrent requests) Transfer rate: 170.10 [Kbytes/sec] received Connection Times (ms) min mean[+/-sd] median max Connect: 0 3 4.6 0 11 Processing: 13 111 29.4 122 161 Waiting: 2 110 29.5 121 159 Total: 13 114 26.1 122 164 Percentage of the requests served within a certain time (ms) 50% 122 66% 124 75% 125 80% 128 90% 136 95% 145 98% 153 99% 160 100% 164 (longest request)

上面结果可以得出,同时并发500请求,压测结果是这样:

Requests per second: 1049.28 [#/sec] (mean) Time per request: 476.515 [ms] (mean) Time per request: 0.953 [ms] (mean, across all concurrent

2|2Gunicorn启动压测


1.使用gunicorn启动, 这里启动4个进程

其中: -w 为开启n个进程 -b 监听地址

[jian@laptop practics]$ gunicorn -w 4 -b 0.0.0.0:8000 demo:app [2020-01-10 21:08:56 +0800] [15563] [INFO] Starting gunicorn 19.7.1 [2020-01-10 21:08:56 +0800] [15563] [INFO] Listening at: http://0.0.0.0:8000 (15563) [2020-01-10 21:08:56 +0800] [15563] [INFO] Using worker: sync [2020-01-10 21:08:56 +0800] [15670] [INFO] Booting worker with pid: 15670 [2020-01-10 21:08:56 +0800] [15671] [INFO] Booting worker with pid: 15671 [2020-01-10 21:08:56 +0800] [15672] [INFO] Booting worker with pid: 15672 [2020-01-10 21:08:56 +0800] [15675] [INFO] Booting worker with pid: 15675

2.使用ab工具进行压测

[jian@laptop practics]$ ab -n 500 -c 500 http://localhost:8000/ This is ApacheBench, Version 2.3 <$Revision: 1843412 $> Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/ Licensed to The Apache Software Foundation, http://www.apache.org/ Benchmarking localhost (be patient) Completed 100 requests Completed 200 requests Completed 300 requests Completed 400 requests Completed 500 requests Finished 500 requests Server Software: gunicorn/19.7.1 Server Hostname: localhost Server Port: 8000 Document Path: / Document Length: 12 bytes Concurrency Level: 500 Time taken for tests: 0.111 seconds Complete requests: 500 Failed requests: 0 Total transferred: 86000 bytes HTML transferred: 6000 bytes Requests per second: 4522.80 [#/sec] (mean) Time per request: 110.551 [ms] (mean) Time per request: 0.221 [ms] (mean, across all concurrent requests) Transfer rate: 759.69 [Kbytes/sec] received Connection Times (ms) min mean[+/-sd] median max Connect: 0 4 6.9 0 16 Processing: 5 21 5.8 22 28 Waiting: 1 21 5.9 22 28 Total: 18 26 4.7 25 41 Percentage of the requests served within a certain time (ms) 50% 25 66% 27 75% 28 80% 28 90% 31 95% 37 98% 40 99% 40 100% 41 (longest request)

上面结果可以得出,同时并发500请求,压测结果是这样:

Requests per second: 4522.80 [#/sec] (mean) Time per request: 110.551 [ms] (mean) Time per request: 0.221 [ms] (mean, across all concurrent

可以明显看到Requests per second: 明显比flask自带的要高 而且Time per request 也少了很多

2|3问题


gunicorn并发比flask好的原因

1.单 Worker

只有一个进程在跑所有的请求,而由于实现的简陋性,内置 webserver 很容易卡死。

并且只有一个 Worker 在跑请求。在多核 CPU 下,仅仅占用一核。

当然,其实也可以多起几个进程。

2.缺乏 Worker 的管理

加入负载量上来了,Gunicorn 可以调节 Worker 的数量

flask内置的 Webserver 是不适合做这种事情的

一言以蔽之,太弱,几个请求就打满了

gunicorn和flask通信 流程
nginx<->gunicorn<->flask

3|0gunicorn 几种 worker 性能测试比较


3|11.Gunicorn目前自带支持几种工作方式:


  • sync (默认值) :最简单的同步工作模式
  • eventlet :基于Greenlet库,利用python协程实现的
  • gevent: 基于Greenlet库,利用python协程实现的
  • tornado: 利用python Tornado框架实现
  • gaiohttp:利用aiohttp库实现异步I/O,支持web socket
  • gthread:线程工作模式,利用线程池管理连接

sync

sync 模式(同步工作模式)
这是最基本的工作模式,也是默认的工作模式,线程为native类型。即请求先来后到,排队模式。

eventlet 模式(协程异步)

eventlet 工作模式是基于eventlet库,利用python协程实现的。
要使用该工作模式的话必须先安装eventlet库,并且版本要大于等于0.24.1

gevent模式(协程异步)

gevent是基于Greentlet库,利用python协程实现的。
Gunicorn允许通过设置对应的worker类来使用这些异步Python库。这里的设置适用于我们想要在单核机器上运行的gevent

tornado模式

tornado利用python Tornado框架来实现。 安装的tornado库的版本要大于等于0.2。

gthread模式

gthread采用的是线程工作模式,利用线程池管理连接,需要安装gthread库。

参数

--worker-class STRTING要使用的工作模式,默认为sync异步 -w INT, --workers INT用于处理工作进程的数量,为正整数,默认为1

3|22.安装测试模块


[jian@laptop tmp]$ cat requirements.txt gunicorn==19.7.1 flask==1.1.1 flask-redis==0.4.0 gevent==1.2.2 tornado==4.5.3 eventlet==0.25.1 #这里要特别注意tornado版本必须是5.0以下,不然gunicorn 在启动会报错: TypeError: __init__() got an unexpected keyword argument 'io_loop'
[jian@laptop tmp]$ pip install -r requirements.txt

3|3测试例子


测试环境: Fedora 29 x64

需要安装redis , 可以使用下面命令进行安装:

[root@laptop ~]# dnf install redis

开启redis服务:

[root@laptop ~]# /usr/bin/redis-server /etc/redis.conf

测试程序:

from flask import Flask from flask_redis import FlaskRedis REDIS_URL = "redis://:@localhost:6379/0" app = Flask(__name__) app.config.from_object(__name__) redis = FlaskRedis(app, True) @app.route('/') def index(): redis.incr("hit", 1) return redis.get("hit") if __name__ == '__main__': app.run()

3|4开始测试


分别使用四种方式开启服务

[jian@laptop practics]$ gunicorn -w 4 demo:app --worker-class sync [jian@laptop practics]$ gunicorn -w 4 demo:app --worker-class gevent [jian@laptop practics]$ gunicorn -w 4 demo:app --worker-class tornado [jian@laptop practics]$ gunicorn -w 4 demo:app --worker-class eventlet

使用ab工具,并行500个客户端,发送50000次请求,压测命令:

[jian@laptop practics]$ ab -c 500 -t 30 -r http://localhost:8000/

3|5测试结果


Worker class Time taken for tests Complete requests Failed requests Requests per second 用户平均请求等待时间 服务器平均处理时间 最小连接时间 平均连接时间 50%的连接时间 最大连接时间
sync 43.362s 49719 157 1146.61 436.069ms 0.872ms 12ms 55ms 25ms 33574ms
gevent 13.062s 50000 0 3827.96 130.618ms 0.261ms 3ms 129ms 96ms 1477ms
tornado 27.925s 50000 17 1790.50 279.252ms 0.559ms 16ms 146ms 27850ms 53547ms
eventlet 12.601s 50000 0 3967.88 126.012ms 0.252ms 9ms 125ms 1377ms 3123ms

eventlet 和gevent两种方式效果最好,数据基本差不多.

4|0gunicorn 配置


4|1workers模式


每个worker都是一个加载python应用程序的UNIX进程 worker之间没有共享内存
建议workers 数量是 (2*CPU) + 1

4|2多线程模式


gunicorn 还允许每个worker拥有多个线程

在这种模式下,每个worker都会加载一次,同一个worker生成的每个线程共享相同的内存空间

使用threads模式,每一次使用threads模式,worker类就会是gthread

gunicorn -w 5 --threads=2 main:app

等同于:

gunicorn -w 5 --thread=2 --worker-class=gthread main:app

最大的并发请求就是worker * 线程 , 也就是10
建议最大并发数 是(2*CPU) +1

4|3伪线程 gevent (协程)


gunicorn --worker-class=gevent --worker-connections=1000 -w 3 main:app

work-connections 是对gevent worker类的特殊设置

建议workers数量 仍然是 (2*CPU) + 1
在这种情况下,最大的并发请求数 是3000(3个worker * 1000连接/worker)

5|0建议


  • IO 受限 -建议使用gevent或者asyncio
  • CPU受限 -建议增加workers数量
  • 不确定内存占用? -建议使用gthread
  • 不知道怎么选择? -建议增加workers数量

6|0其他备注


Gunicorn对静态文件的支持不太好,所以生产环境下常用Nginx作为反向代理服务器。

生产环境都是Nginx + gunicorn + flask

7|0Fastapi 和 gunicorn


fastapi 自带一个叫做 uvicorn的 wsgi 服务器,通常用于开发调试。生产环境中主要使用gunicorn作为wsgi。

生产环境中使用gunicorn的方式如下:

gunicorn -w 1 -k uvicorn.workers.UvicornWorker -c gunicorn_conf.py main:app

fastapi要在工作进程中使用的与gunicorn兼容的工作类。fastapi中需要使用 uvicorn.workers.UvicornWorker

Uvicorn为单进程的ASGI server,而Gunicorn是管理运行多个Uvicorn,以达到并发与并行的最好效果在fastapi中,使用gunicorn管理uvicorn,gunicorn可以启动多个uvicorn进程,并管理这些进程。


__EOF__

本文作者goldsunshine
本文链接https://www.cnblogs.com/goldsunshine/p/16979098.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   金色旭光  阅读(2085)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
点击右上角即可分享
微信分享提示