gunicorn 高性能wsgi服务器
参考: https://zhuanlan.zhihu.com/p/102716258
Gunicorn是什么
Gunicorn Green Unicorn 是一个 UNIX 下的 WSGI HTTP 服务器,它是一个 移植自 Ruby 的 Unicorn 项目的 pre-fork worker 模型。
Gunicorn启动项目之后一定会有一个主进程Master和一个或者多个工作进程。工作进程的数量可以指定。主进程是维护服务器的运行,工作进程是实际处理请求的进程。所有请求和响应均由 Worker 处理。
同步的 Worker 一次处理一个请求。
gunicorn flask 压测对比
测试例子: demo.py
from flask import Flask
app = Flask(__name__)
@app.route('/')
def index():
return 'hello world!'
if __name__ == '__main__':
app.run()
Flask启动压测
直接运行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
Gunicorn启动压测
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 也少了很多
问题
gunicorn并发比flask好的原因
1.单 Worker
只有一个进程在跑所有的请求,而由于实现的简陋性,内置 webserver 很容易卡死。
并且只有一个 Worker 在跑请求。在多核 CPU 下,仅仅占用一核。
当然,其实也可以多起几个进程。
2.缺乏 Worker 的管理
加入负载量上来了,Gunicorn 可以调节 Worker 的数量
flask内置的 Webserver 是不适合做这种事情的
一言以蔽之,太弱,几个请求就打满了
gunicorn和flask通信 流程
nginx<->gunicorn<->flask
gunicorn 几种 worker 性能测试比较
1.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;
2.安装测试模块
[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
测试例子
测试环境: 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()
开始测试
分别使用四种方式开启服务
[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/
测试结果
| 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两种方式效果最好,数据基本差不多.
gunicorn 配置
workers模式
每个worker都是一个加载python应用程序的UNIX进程 worker之间没有共享内存
建议workers 数量是 (2*CPU) + 1
多线程模式
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
伪线程 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)
建议
- IO 受限 -建议使用gevent或者asyncio
- CPU受限 -建议增加workers数量
- 不确定内存占用? -建议使用gthread
- 不知道怎么选择? -建议增加workers数量
其他备注
Gunicorn对静态文件的支持不太好,所以生产环境下常用Nginx作为反向代理服务器。
生产环境都是Nginx + gunicorn + flask
Fastapi 和 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进程,并管理这些进程。
gunicorn 高性能wsgi服务器的更多相关文章
- 分享关于搭建高性能WEB服务器的一篇文章
这篇文章主要介绍了Centos5.4+Nginx-0.8.50+UWSGI-0.9.6.2+Django-1.2.3搭建高性能WEB服务器的相关资料,需要的朋友可以参考下(http://m.0813s ...
- Netty实现高性能RPC服务器优化篇之消息序列化
在本人写的前一篇文章中,谈及有关如何利用Netty开发实现,高性能RPC服务器的一些设计思路.设计原理,以及具体的实现方案(具体参见:谈谈如何使用Netty开发实现高性能的RPC服务器).在文章的最后 ...
- SSDB:高性能数据库服务器
SSDB是一个开源的高性能数据库服务器, 使用Google LevelDB作为存储引擎, 支持T级别的数据, 同时支持类似Redis中的zset和hash等数据结构, 在同时需求高性能和大数据的条件下 ...
- NGINX高性能Web服务器详解(读书笔记)
原文地址:NGINX高性能Web服务器详解(读书笔记) 作者:夏寥寥 第4章 Nginx服务器的高级配置 4.1 针对IPv4的内核7个参数的配置优化 说明:我们可以将这些内核参数的值追加到Linu ...
- 高性能图片服务器–ZIMG
2011年李彦宏在百度联盟峰会上就提到过互联网的读图时代已经到来1,图片服务早已成为一个互联网应用中占比很大的部分,对图片的处理能力也相应地变成企业和开发者的一项基本技能.需要处理海量图片的典型应用有 ...
- 高性能Linux服务器 第11章 构建高可用的LVS负载均衡集群
高性能Linux服务器 第11章 构建高可用的LVS负载均衡集群 libnet软件包<-依赖-heartbeat(包含ldirectord插件(需要perl-MailTools的rpm包)) l ...
- 高性能Linux服务器 第10章 基于Linux服务器的性能分析与优化
高性能Linux服务器 第10章 基于Linux服务器的性能分析与优化 作为一名Linux系统管理员,最主要的工作是优化系统配置,使应用在系统上以最优的状态运行.但硬件问题.软件问题.网络环境等 ...
- 高性能Linux服务器 第6章 ext3文件系统反删除利器ext3grep extundelete工具恢复rm -rf 误删除的文件
高性能Linux服务器 第6章 ext3文件系统反删除利器ext3grep extundelete工具恢复rm -rf 误删除的文件 只能用于ext3文件系统!!!!!!!高俊峰(高性能Linux ...
- 优化系统资源ulimit《高性能Linux服务器构建实战:运维监控、性能调优与集群应用》
优化系统资源ulimit<高性能Linux服务器构建实战:运维监控.性能调优与集群应用> 假设有这样一种情况,一台Linux 主机上同时登录了10个用户,在没有限制系统资源的情况下,这10 ...
- 优化Linux内核参数/etc/sysctl.conf sysctl 《高性能Linux服务器构建实战:运维监控、性能调优与集群应用》
优化Linux内核参数/etc/sysctl.conf sysctl <高性能Linux服务器构建实战:运维监控.性能调优与集群应用> http://book.51cto.com/ar ...
随机推荐
- 【Unity】 ScriptableObject ——生成多个ScriptableObject作为子对象,可以点击展开并显示二级菜单
官方是这么介绍ScriptabelObject的: "ScriptableObject 是一个可独立于类实例来保存大量数据的数据容器.ScriptableObject 的一个主要用例是通过避 ...
- Keil MDK忽略警告, 包括文件末尾空白行, 未使用等警告
首先应该了解为什么Keil MDK 会有这样的警告, 原因简单说就是C99规定了要在末尾行加回车 一. 你可以使用格式化工具对所有源文件进行一次格式化处理. 二. 在Keil MDK中查看 Build ...
- Python 在PDF中生成水印
前言 在PDF中插入水印是比较常用的一种功能.一般在生成比较重要的,或者需要注明版权.作者的文档时使用比较多. 这里我将分享一个通过python代码为PDF文档添加水印的办法(包括文本水印和图像水印) ...
- Static关键词
在程序中使用static 变量 1. 局部变量 普通局部变量是再熟悉不过的变量了,在任何一个函数内部定义的变量(不加static修饰符)都属于这个范畴.编译器一般不对普通局部变量进行初始化,也就是说它 ...
- 开源推荐,灵活多变功能强大的CMDB
一个完善的基础资源数据库是我们构建运维自动化上层应用的基础,所以构建CMDB系统成了有想法在DevOps运维自动化领域有所发展的企业离不开的重要一环,但受制于每家企业不同的内外部环境.资源配置以及管理 ...
- MySQL的索引为什么使用B+树而不使用跳表?
目录 MySQL的索引为什么使用B+树而不使用跳表? 1.B+树的结构 2.跳表的结构 3.B+树和跳表的区别 1.B+树新增数据会怎么样 跳表新增数据 4.Mysql的索引为什么使用B+树而不使用跳 ...
- 神经网络入门篇:详解随机初始化(Random+Initialization)
当训练神经网络时,权重随机初始化是很重要的.对于逻辑回归,把权重初始化为0当然也是可以的.但是对于一个神经网络,如果把权重或者参数都初始化为0,那么梯度下降将不会起作用. 来看看这是为什么. 有两个输 ...
- Linux笔记03: Linux常用命令_3.2目录操作命令
3.2 目录操作命令 3.2.1 ls命令 ●命令名称:ls. ●英文原意:list directory contents. ●所在路径:/usr/bin/ls. ●执行权限:所有用户. ●功能描述: ...
- 文心一言 VS 讯飞星火 VS chatgpt (160)-- 算法导论12.4 2题
二.用go语言,请描述这样一棵有 n 个结点的二叉搜索树,其树中结点的平均深度为 O(lgn),但这棵树的高度是w(lgn).一棵有 n个结点的二叉搜索树中结点的平均深度为 O(lgn),给出这棵树高 ...
- 使用Py2neo更新Neo4j中节点的属性值的正确姿势
1 def findNode(name, graph): 2 matcher = NodeMatcher(graph) 3 m = matcher.match(name = name).first() ...