文章目录
gunicorn部署
1、简介
flask 自带的web服务器可用于开发环境运行调试,不适合部署在生产环境,无法满足线上的性能要求。当使用app.run(host = '0.0.0.0',port=6000)
启动时,flask框架会有一段 WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
先介绍下几个概念,方便理解使用 wsgi server 部署的意义:
- WSGI: 全称是Web Server Gateway Interface(web服务器网关接口),它是一种规范,它是web服务器和web应用程序之间的接口。它的作用就像是桥梁,连接在web服务器和web应用框架之间。
- uwsgi: 是一种传输协议,用于定义传输信息的类型。
- uWSGI: 是实现了uwsgi协议WSGI的web服务器。
gunicorn是一个python WSGI http server,我们这里采用它做 wsgi 服务器,来部署flask程序。
2、模块安装
pip install gunicorn
一般使用它,主要是为使用其异步的worker模型,还需要安装对应的异步模块。
pip install greenlet # 使用异步必须安装
pip install eventlet # 使用eventlet workers
pip install gevent # 使用gevent workers
3、启动命令
3.1 命令行配置 gunicorn 参数
gunicorn 命令启动程序比较简单。以下面 main.py 为例
from flask import Flask
app = Flask(__name__)
@app.route('/',methods=['GET'])
def hello_world():
return 'Hello World!'
if __name__ == '__main__':
app.run(host='0.0.0.0', port=6000)
运行命令:
gunicorn -w 5 -b 0.0.0.0:6000 -t 120 main:app
持久化运行,使用nohup,运行日志将存储于当前目录的app.log日志
nohup python -m gunicorn -w 5 -b 0.0.0.0:6000 -t 120 main:app > app.log 2>&1 &
虽然 gunicorn 是 Unix 环境下的工具,但可以通过 Windows Subsystem for Linux (WSL) 在 Windows 上运行。
在 WSL 中启动 Flask 应用:
gunicorn -w 5 -k gevent -b 0.0.0.0:6000 -t 120 main:app
解释下参数含义:
-w
:表示工作进程数
-b
:访问地址和端口
-t
:设置超时时间120秒,默认30秒
-k gevent
:表示使用 gevent 作为 worker 类型
main
:flask启动python文件名
app
:脚本中创建的Flask对象名
注意:1、windows系统会报错:ModuleNotFoundError: No module named 'fcntl'
,原因是 gunicorn 不支持windows,在 linux 上可正常运行。
2、若遇到flask启动后,访问请求很慢,一般是 gunicorn 和 flask-socketio 版本不兼容,找到对应版本即可,我安装的都是最新版,没有问题。
如果生产环境,必不可少还需要配置日志信息,如下:
gunicorn -w 4 -b 0.0.0.0:8080 --access-logfile access.log --error-logfile error.log app:app -D
-D
表示将gunicorn置于后台运行,可以通过tail -f access.log
或者tail -f error.log
查看记录的日志信息。
3.2 一些其他的Gunicorn命令示例
- 运行一个名为
myapp.py
的Flask应用程序,启用访问日志和错误日志:
gunicorn --access-logfile access.log --error-logfile error.log myapp:app
- 运行一个名为
myapp.py
的Flask应用程序,以守护进程模式运行:
gunicorn -D myapp:app
- 运行一个名为
myapp.py
的Flask应用程序,指定配置文件:
gunicorn -c gunicorn.conf.py myapp:app
- 重新加载正在运行的Gunicorn实例(平滑重启):
kill -HUP <主进程ID>
- 停止正在运行的Gunicorn实例(优雅停止):
kill -TERM <主进程ID>
- 运行一个名为
myapp.py
的Flask应用程序,设置工作进程的最大请求数:
gunicorn --max-requests 1000 myapp:app
- 运行一个名为
myapp.py
的Flask应用程序,设置工作进程的最大请求数波动范围:
gunicorn --max-requests-jitter 50 myapp:app
- 运行一个名为
myapp.py
的Flask应用程序,设置工作进程的名称前缀:
gunicorn --worker-tmp-dir /dev/shm myapp:app
- 运行一个名为
myapp.py
的Flask应用程序,设置工作进程的临时目录:
gunicorn --worker-tmp-dir /dev/shm myapp:app
- 运行一个名为
myapp.py
的Flask应用程序,启用SSL支持:
gunicorn --certfile=server.crt --keyfile=server.key myapp:app
- 运行一个名为
myapp.py
的Flask应用程序,设置日志记录级别:
gunicorn --log-level debug myapp:app
- 运行一个名为
myapp.py
的Flask应用程序,启用代理协议支持:
gunicorn --proxy-protocol myapp:app
- 运行一个名为
myapp.py
的Flask应用程序,设置请求头大小限制:
gunicorn --limit-request-line 8190 myapp:app
- 运行一个名为
myapp.py
的Flask应用程序,设置请求字段数量限制:
gunicorn --limit-request-fields 100 myapp:app
- 运行一个名为
myapp.py
的Flask应用程序,设置请求字段大小限制:
gunicorn --limit-request-field_size 8190 myapp:app
请注意,上面的命令中的myapp:app
表示您的应用程序位于名为myapp.py
的文件中,并且Flask应用程序实例的名称为app
。您需要根据您自己的应用程序进行相应修改。
3.3 文件配置 gunicorn 参数
3.3.1进程+线程模式
根目录新建配置文件 config.py
# 是否开启debug模式
debug = True
# 访问地址
bind = "0.0.0.0:6000"
# 工作进程数
workers = 2
# 工作线程数
threads = 2
# 超时时间
timeout = 600
# 输出日志级别
loglevel = 'debug'
# 存放日志路径
pidfile = "log/gunicorn.pid"
# 存放日志路径
accesslog = "log/access.log"
# 存放日志路径
errorlog = "log/debug.log"
# gunicorn + apscheduler场景下,解决多worker运行定时任务重复执行的问题
preload_app = True
运行命令:
gunicorn -c config.py main:app
看到以下信息表示启动成功
配置文件参数详解:
-c CONFIG : CONFIG,配置文件的路径,通过配置文件启动;生产环境使用;
-b ADDRESS : ADDRESS,ip加端口,绑定运行的主机;
-w INT, --workers INT:用于处理工作进程的数量,为正整数,默认为1;
-k STRTING, --worker-class STRTING:要使用的工作模式,默认为sync异步,可以下载eventlet和gevent并指定
--threads INT:处理请求的工作线程数,使用指定数量的线程运行每个worker。为正整数,默认为1。
--worker-connections INT:最大客户端并发数量,默认情况下这个值为1000。
--backlog int:未决连接的最大数量,即等待服务的客户的数量。默认2048个,一般不修改;
-p FILE, --pid FILE:设置pid文件的文件名,如果不设置将不会创建pid文件
--access-logfile FILE : 要写入的访问日志目录
--access-logformat STRING:要写入的访问日志格式
--error-logfile FILE, --log-file FILE : 要写入错误日志的文件目录。
--log-level LEVEL : 错误日志输出等级。
--limit-request-line INT : HTTP请求头的行数的最大大小,此参数用于限制HTTP请求行的允许大小,默认情况下,这个值为4094。值是0~8190的数字。
--limit-request-fields INT : 限制HTTP请求中请求头字段的数量。此字段用于限制请求头字段的数量以防止DDOS攻击,默认情况下,这个值为100,这个值不能超过32768
--limit-request-field-size INT : 限制HTTP请求中请求头的大小,默认情况下这个值为8190字节。值是一个整数或者0,当该值为0时,表示将对请求头大小不做限制
-t INT, --timeout INT:超过这么多秒后工作将被杀掉,并重新启动。一般设定为30秒;
--daemon: 是否以守护进程启动,默认false;
--chdir: 在加载应用程序之前切换目录;
--graceful-timeout INT:默认情况下,这个值为30,在超时(从接收到重启信号开始)之后仍然活着的工作将被强行杀死;一般使用默认;
--keep-alive INT:在keep-alive连接上等待请求的秒数,默认情况下值为2。一般设定在1~5秒之间。
--reload:默认为False。此设置用于开发,每当应用程序发生更改时,都会导致工作重新启动。
--spew:打印服务器执行过的每一条语句,默认False。此选择为原子性的,即要么全部打印,要么全部不打印;
--check-config :显示现在的配置,默认值为False,即显示。
-e ENV, --env ENV: 设置环境变量;
3.3.2进程+协程模式
根目录新建配置文件 config.py
from gevent import monkey
monkey.patch_all()
# 是否开启debug模式
debug = True
# 访问地址
bind = "0.0.0.0:6000"
# 工作进程数
workers = 2
# 设置协程模式
worker_class="gevent"
# 最大客户端并发数量,默认情况下这个值为1000。此设置将影响gevent和eventlet工作模式
worker_connections=500
# 超时时间
timeout = 600
# 输出日志级别
loglevel = 'debug'
# 存放日志路径
pidfile = "log/gunicorn.pid"
# 存放日志路径
accesslog = "log/access.log"
# 存放日志路径
errorlog = "log/debug.log"
# gunicorn + apscheduler场景下,解决多worker运行定时任务重复执行的问题
preload_app = True
运行命令:
gunicorn -c config.py main:app
看到 using worker :gevent 模式启动
使用 gunicorn 部署到 docker 容器也比较方便,只要将启动命令写到 dockerfile 里即可。
其它可配置项:config.py
import logging
import logging.handlers
import os
import multiprocessing
import gevent.monkey
gevent.monkey.patch_all()
bind = '0.0.0.0:8080' # 绑定的ip已经端口号
chdir = '/home/flaskProject' # gunicorn要切换到的目的工作目录
timeout = 60 # 超时
worker_class = 'gevent' # 使用gevent模式,还可以使用sync 模式,默认的是sync模式
workers = multiprocessing.cpu_count() * 2 + 1 # 启动的进程数
loglevel = "info" # 日志级别,这个日志级别指的是错误日志的级别,而访问日志的级别无法设置
access_log_format = '%(t)s %(p)s %(h)s "%(r)s" %(s)s %(L)s %(b)s %(f)s" "%(a)s"' # 设置gunicorn访问日志格式,错误日志无法设置
pidfile = "gunicorn.pid"
accesslog = "access.log"
errorlog = "error.log"
daemon = True # 是否后台运行
启动后项目的目录下会生成access.log,error.log
和gunicorn.pid
三个文件,gunicorn.pid
中保存了gunicorn的主进程PID号,可以通过cat gunicorn.pid
查看,当想要停止gunicorn时,直接kill 进程号
即可杀死所有gunicorn进程。
4、结束gunicorn服务进程
使用ps -ef | grep gunicorn
命令找出gunicorn所有进程。
[root@VM_0_12_centos ~]# ps -ef | grep gunicorn
root 16843 23035 0 Oct14 ? 00:00:02 /root/Envs/myflask/bin/python3.6 /root/Envs/myflask/bin/gunicorn -w 3 -b 172.17.0.12:80 app:app
root 22445 23035 0 Oct04 ? 00:00:15 /root/Envs/myflask/bin/python3.6 /root/Envs/myflask/bin/gunicorn -w 3 -b 172.17.0.12:80 app:app
root 22581 23035 0 Oct11 ? 00:00:05 /root/Envs/myflask/bin/python3.6 /root/Envs/myflask/bin/gunicorn -w 3 -b 172.17.0.12:80 app:app
root 23035 1 0 Sep27 ? 00:04:11 /root/Envs/myflask/bin/python3.6 /root/Envs/myflask/bin/gunicorn -w 3 -b 172.17.0.12:80 app:app
然后使用 kill -9 进程ID
命令来杀掉进程,注意,我们找到主进程杀掉即可,子进程会随之结束,在上例中,主进程号为23035.
[root@VM_0_12_centos ~]# kill -9 23035
[root@VM_0_12_centos ~]# ps -ef | grep gunicorn
杀掉进程后,稍等几秒,再使用ps -ef | grep gunicorn
查看,发现gunicorn服务进程已全部杀掉。
使用 waitress
waitress 是一个纯 Python 的 WSGI 服务器,支持 Windows 环境,并且可以处理多进程。
首先,安装 waitress:
pip install waitress
然后,使用 waitress 启动 Flask 应用:
from flask import Flask
from waitress import serve
app = Flask(__name__)
@app.route('/')
def hello():
return "Hello, World!"
if __name__ == '__main__':
serve(app, host='0.0.0.0', port=5000, threads=4)
其中 threads=4 表示启动 4 个线程处理请求。
使用 uvicorn 和 hypercorn
uvicorn 和 hypercorn 是支持 ASGI 的服务器,可以在 Windows 上运行,并且支持多进程。
首先,安装 uvicorn 或 hypercorn:
pip install uvicorn
# 或者
pip install hypercorn
然后,使用 uvicorn 或 hypercorn 启动 Flask 应用:
uvicorn your_app:app --workers 4 --host 0.0.0.0 --port 9527
# 或者
hypercorn your_app:app --workers 4 --bind 0.0.0.0:5000
其中 --workers 4
表示启动 4 个工作进程。
查看帮助
uvicorn --help
hypercorn --help
并行测试
- server端
from flask import Flask, render_template
from gevent import pywsgi,monkey
import time
import json
import waitress
# monkey.patch_all()
app = Flask(__name__)
# 使用 Quart(Flask 的异步版本)
# from quart import Quart
# app = Quart(__name__)
@app.route('/')
def connect():
return "connected test"
@app.route('/index')
def index_test():
time0 = time.time()
for i in range(10000):
j = list(range(1000))
print(time.time() - time0)
res = {'data': 1}
res = json.dumps(res)
return res
if __name__ == "__main__":
# wsgi 同步方法
# server = pywsgi.WSGIServer(("0.0.0.0", 5000), app)
# server.serve_forever()
# 使用waitress异步方法
waitress.serve(app, host='0.0.0.0', port=5000, threads=8)
# 使用Quart(__name__) 异步方法 启动:uvicorn server_demo:app --host 0.0.0.0 --port 5000 --workers 4
# app.run(host='0.0.0.0', port=5000, debug=False)
print("Server started")
- client端
import requests
from threading import Thread
import time
def req():
time0 = time.time()
res = requests.get('http://127.0.0.1:5000/index')
print(time.time() - time0)
# print(res.text)
for i in range(5):
th = Thread(target=req)
th.start()
参考:https://www.cnblogs.com/shenh/p/16824903.html
https://blog.csdn.net/Woodrow1994/article/details/113831985
https://zhuanlan.zhihu.com/p/618533248
https://zhuanlan.zhihu.com/p/488458470
http://www.manongjc.com/detail/12-nhpgmgnknsiysxf.html
https://blog.csdn.net/lukem44/article/details/133775080