第一个python flask web服务实践

本文参考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.logapp_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
]

重新安装 gunicorngevent,即:

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
    成功日志与上一个方案的一致。
原文地址:https://www.cnblogs.com/shoren/p/15238178.html