本文参考flask+Gunicorn 的实例和配置,在本地启动flask服务。
Gunicorn 配置可以参考python gunicorn详解
首先看下测试代码结构:
- web
- __init__.py
- access.log
- app_run.log
- flask_simple.py
- gunicorn_config.py
- meinheld_gunicorn_config.py
单flask
- 编写
flask_simple.py
文件:
from flask import Flask, request
import time
my_app = Flask(__name__) #flask实例名字为 my_app
my_app.config['JSON_AS_ASCII'] = False
# http://127.0.0.1:5001/app,这里装饰方法的名字与实例名字一致:my_app
@my_app.route('/app', methods=['GET'])
def func():
# time.sleep(5)
return 'simple flask demo'
if __name__ == "__main__":
my_app.run() #默认http://127.0.0.1:5000/,启动使用实例名字 my_app
- PyCharm 启动,启动日志:
* Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
* Serving Flask app "flask_simple" (lazy loading)
* Environment: production
WARNING: Do not use the development server in a production environment.
Use a production WSGI server instead.
* Debug mode: off
- terminal执行:
python flask_simple.py
,启动日志:
* Serving Flask app "flask_simple" (lazy loading)
* Environment: production
WARNING: Do not use the development server in a production environment.
Use a production WSGI server instead.
* Debug mode: off
* Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
- 浏览器请求
http://127.0.0.1:5000/app
,返回simple flask demo
gunicorn+gevent
- 编写文件
gunicorn_config.py
,如下:
# 多进程
import multiprocessing
"""gunicorn+gevent 的配置文件"""
'''执行:gunicorn -c gunicorn_config.py flask_simple:my_app
'''
# 预加载资源
preload_app = True
# 绑定 ip + 端口
bind = "127.0.0.1:5000"
# 进程数 = cup数量 * 2 + 1
workers = multiprocessing.cpu_count() * 2 + 1
# 线程数 = cup数量 * 2
threads = multiprocessing.cpu_count() * 2
# 等待队列最大长度,超过这个长度的链接将被拒绝连接
backlog = 2048
# 工作模式--协程
worker_class = "gevent"
# 最大客户客户端并发数量,对使用线程和协程的worker的工作有影响
# 服务器配置设置的值 1200:中小型项目 上万并发: 中大型
# 服务器硬件:宽带+数据库+内存
# 服务器的架构:集群 主从
worker_connections = 1200
# 进程名称
proc_name = 'gunicorn.pid'
# 进程pid记录文件
pidfile = 'app_run.log'
# 日志等级
loglevel = 'debug'
# 日志文件名
logfile = 'debug.log'
# 访问记录
accesslog = 'access.log'
# 访问记录格式
access_log_format = '%(h)s %(t)s %(U)s %(q)s'
- terminal 执行
gunicorn -c gunicorn_config.py flask_simple:my_app
。 gunicorn_config.py
为配置文件的名字flask_simple
是模块名,要启动的文件名字,即flask_simple.py
的文件名my_app
为Flask实例名字,见flask_simple.py内。
查看 运行成功日志
[2021-09-06 23:20:28 -0700] [23069] [DEBUG] Current configuration:
config: gunicorn_config.py
wsgi_app: None
bind: ['127.0.0.1:5000']
backlog: 2048
workers: 25
worker_class: gevent
threads: 24
worker_connections: 1200
max_requests: 0
max_requests_jitter: 0
timeout: 30
graceful_timeout: 30
keepalive: 2
limit_request_line: 4094
limit_request_fields: 100
limit_request_field_size: 8190
reload: False
reload_engine: auto
reload_extra_files: []
spew: False
check_config: False
print_config: False
preload_app: True
sendfile: None
reuse_port: False
chdir: /Users/danxiao/python/study_demo/web
daemon: False
raw_env: []
pidfile: app_run.log
worker_tmp_dir: None
user: 501
group: 20
umask: 0
initgroups: False
tmp_upload_dir: None
secure_scheme_headers: {'X-FORWARDED-PROTOCOL': 'ssl', 'X-FORWARDED-PROTO': 'https', 'X-FORWARDED-SSL': 'on'}
forwarded_allow_ips: ['127.0.0.1']
accesslog: access.log
disable_redirect_access_to_syslog: False
access_log_format: %(h)s %(t)s %(U)s %(q)s
errorlog: -
loglevel: debug
capture_output: False
logger_class: gunicorn.glogging.Logger
logconfig: None
logconfig_dict: {}
syslog_addr: unix:///var/run/syslog
syslog: False
syslog_prefix: None
syslog_facility: user
enable_stdio_inheritance: False
statsd_host: None
dogstatsd_tags:
statsd_prefix:
proc_name: gunicorn.pid
default_proc_name: flask_simple:my_app
pythonpath: None
paste: None
on_starting: <function OnStarting.on_starting at 0x1020d2830>
on_reload: <function OnReload.on_reload at 0x1020d2950>
when_ready: <function WhenReady.when_ready at 0x1020d2a70>
pre_fork: <function Prefork.pre_fork at 0x1020d2b90>
post_fork: <function Postfork.post_fork at 0x1020d2cb0>
post_worker_init: <function PostWorkerInit.post_worker_init at 0x1020d2dd0>
worker_int: <function WorkerInt.worker_int at 0x1020d2ef0>
worker_abort: <function WorkerAbort.worker_abort at 0x1020e9050>
pre_exec: <function PreExec.pre_exec at 0x1020e9170>
pre_request: <function PreRequest.pre_request at 0x1020e9290>
post_request: <function PostRequest.post_request at 0x1020e9320>
child_exit: <function ChildExit.child_exit at 0x1020e9440>
worker_exit: <function WorkerExit.worker_exit at 0x1020e9560>
nworkers_changed: <function NumWorkersChanged.nworkers_changed at 0x1020e9680>
on_exit: <function OnExit.on_exit at 0x1020e97a0>
proxy_protocol: False
proxy_allow_ips: ['127.0.0.1']
keyfile: None
certfile: None
ssl_version: 2
cert_reqs: 0
ca_certs: None
suppress_ragged_eofs: True
do_handshake_on_connect: False
ciphers: None
raw_paste_global_conf: []
strip_header_spaces: False
[2021-09-06 23:20:28 -0700] [23069] [INFO] Starting gunicorn 20.1.0
[2021-09-06 23:20:28 -0700] [23069] [DEBUG] Arbiter booted
[2021-09-06 23:20:28 -0700] [23069] [INFO] Listening at: http://127.0.0.1:5000 (23069)
[2021-09-06 23:20:28 -0700] [23069] [INFO] Using worker: gevent
[2021-09-06 23:20:28 -0700] [23075] [INFO] Booting worker with pid: 23075
[2021-09-06 23:20:28 -0700] [23076] [INFO] Booting worker with pid: 23076
[2021-09-06 23:20:28 -0700] [23077] [INFO] Booting worker with pid: 23077
[2021-09-06 23:20:28 -0700] [23078] [INFO] Booting worker with pid: 23078
[2021-09-06 23:20:28 -0700] [23080] [INFO] Booting worker with pid: 23080
[2021-09-06 23:20:28 -0700] [23081] [INFO] Booting worker with pid: 23081
[2021-09-06 23:20:28 -0700] [23083] [INFO] Booting worker with pid: 23083
[2021-09-06 23:20:28 -0700] [23084] [INFO] Booting worker with pid: 23084
[2021-09-06 23:20:28 -0700] [23085] [INFO] Booting worker with pid: 23085
[2021-09-06 23:20:28 -0700] [23086] [INFO] Booting worker with pid: 23086
[2021-09-06 23:20:29 -0700] [23087] [INFO] Booting worker with pid: 23087
[2021-09-06 23:20:29 -0700] [23088] [INFO] Booting worker with pid: 23088
[2021-09-06 23:20:29 -0700] [23089] [INFO] Booting worker with pid: 23089
[2021-09-06 23:20:29 -0700] [23090] [INFO] Booting worker with pid: 23090
[2021-09-06 23:20:29 -0700] [23091] [INFO] Booting worker with pid: 23091
[2021-09-06 23:20:29 -0700] [23092] [INFO] Booting worker with pid: 23092
[2021-09-06 23:20:29 -0700] [23093] [INFO] Booting worker with pid: 23093
[2021-09-06 23:20:29 -0700] [23094] [INFO] Booting worker with pid: 23094
[2021-09-06 23:20:29 -0700] [23095] [INFO] Booting worker with pid: 23095
[2021-09-06 23:20:29 -0700] [23096] [INFO] Booting worker with pid: 23096
[2021-09-06 23:20:29 -0700] [23100] [INFO] Booting worker with pid: 23100
[2021-09-06 23:20:29 -0700] [23101] [INFO] Booting worker with pid: 23101
[2021-09-06 23:20:29 -0700] [23102] [INFO] Booting worker with pid: 23102
[2021-09-06 23:20:29 -0700] [23103] [INFO] Booting worker with pid: 23103
[2021-09-06 23:20:29 -0700] [23104] [INFO] Booting worker with pid: 23104
[2021-09-06 23:20:29 -0700] [23069] [DEBUG] 25 workers
-
运行成功之后,可以看到新生成的两个文件
access.log
和app_run.log
:- 浏览器中执行
http://127.0.0.1:5000/app
都会记录在access.log
中。 - 本服务的pid保存在
app_run.log
中,打开可以看到pid。如果要关闭服务,terminal执行kill -9 {pid}
- 浏览器中执行
-
如果执行报错:
Error: class uri 'gevent' invalid or not found:
[Traceback (most recent call last):
File "/usr/local/lib/python3.7/site-packages/gunicorn/workers/ggevent.py", line 13, in <module>
import gevent
ModuleNotFoundError: No module named 'gevent'
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/usr/local/lib/python3.7/site-packages/gunicorn/util.py", line 99, in load_class
mod = importlib.import_module('.'.join(components))
File "/usr/local/Cellar/python@3.7/3.7.10_3/Frameworks/Python.framework/Versions/3.7/lib/python3.7/importlib/__init__.py", line 127, in import_module
return _bootstrap._gcd_import(name[level:], package, level)
File "<frozen importlib._bootstrap>", line 1006, in _gcd_import
File "<frozen importlib._bootstrap>", line 983, in _find_and_load
File "<frozen importlib._bootstrap>", line 967, in _find_and_load_unlocked
File "<frozen importlib._bootstrap>", line 677, in _load_unlocked
File "<frozen importlib._bootstrap_external>", line 728, in exec_module
File "<frozen importlib._bootstrap>", line 219, in _call_with_frames_removed
File "/usr/local/lib/python3.7/site-packages/gunicorn/workers/ggevent.py", line 15, in <module>
raise RuntimeError("gevent worker requires gevent 1.4 or higher")
RuntimeError: gevent worker requires gevent 1.4 or higher
]
重新安装 gunicorn
和 gevent
,即:
pip3 uninstall gunicorn
pip3 install gunicorn
pip3 uninstall gevent
pip3 install gevent
meinheld + gunicorn + flask
- 编写
meinheld_gunicorn_config.py
文件,与上一个配置不同仅在于worker_class
设置不一样,如下:
import multiprocessing
# gunicorn+meinheld 的配置文件
# 运行方式 命令行 gunicorn -c meinheld_gunicorn_config.py flask_simple:my_app
# 预加载资源
preload_app = True
# 绑定
bind = "0.0.0.0:5000"
# 进程数: cup数量 * 2 + 1
workers = multiprocessing.cpu_count() * 2 + 1
# 线程数 cup数量 * 2
threads = multiprocessing.cpu_count() * 2
# 等待队列最大长度,超过这个长度的链接将被拒绝连接
backlog = 2048
# 工作模式
worker_class = "egg:meinheld#gunicorn_worker"
# 最大客户客户端并发数量,对使用线程和协程的worker的工作有影响
worker_connections = 1200
# 进程名称
proc_name = 'gunicorn.pid'
# 进程pid记录文件
pidfile = 'app_run.log'
# 日志等级
loglevel = 'debug'
# 日志文件名
logfile = 'debug.log'
# 访问记录
accesslog = 'access.log'
# 访问记录格式
access_log_format = '%(h)s %(t)s %(U)s %(q)s'
- 安装
pip3 install meinheld
, 报错:
Collecting meinheld
Using cached meinheld-1.0.2-cp37-cp37m-macosx_11_0_x86_64.whl
Collecting greenlet<0.5,>=0.4.5
Using cached greenlet-0.4.17-cp37-cp37m-macosx_11_0_x86_64.whl
Installing collected packages: greenlet, meinheld
Attempting uninstall: greenlet
Found existing installation: greenlet 1.1.1
Uninstalling greenlet-1.1.1:
Successfully uninstalled greenlet-1.1.1
ERROR: pip's dependency resolver does not currently take into account all the packages that are installed. This behaviour is the source of the following dependency conflicts.
sqlalchemy 1.4.11 requires greenlet!=0.4.17; python_version >= "3", but you have greenlet 0.4.17 which is incompatible.
gevent 21.8.0 requires greenlet<2.0,>=1.1.0; platform_python_implementation == "CPython", but you have greenlet 0.4.17 which is incompatible.
Successfully installed greenlet-0.4.17 meinheld-1.0.2
其实是 meinheld 包依赖 greenlet 版本号范围为 [0.4.5, 0.5),而 gevent 依赖 greenlet 版本号范围为 [1.1.0, 2.0) 两者有冲突。可以使用虚拟环境解决。
- 卸载本环境的 meinheld:
pip3 uninstall meinheld
- 使用conda 创建并激活一个新的虚拟环境:
conda activate xiaoju
- 安装meinheld:
pip3 install meinheld
- 执行:
gunicorn -c meinheld_gunicorn_config.py flask_simple:my_app
成功日志与上一个方案的一致。