• 第一个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
      成功日志与上一个方案的一致。
  • 相关阅读:
    不足百行代码 实体数组转DataTable通用类
    【翻译】WEB安全设计规范(4.1)
    也为读者说几句(兼为什么要骂烂书译者)
    重用之前应仔细分析问题用错轮子有感
    最长代码有多长:不符[单一职责原则(SRP)]的常见设计
    "千里之堤毁于蚁穴"重点项目不能交付之谜(一)泥淖中的验收测试
    企业快速开发框架基于配置文件
    从面试题看高级软件工程师需要哪些技艺
    面试英语【转】
    测试
  • 原文地址:https://www.cnblogs.com/shoren/p/15238178.html
Copyright © 2020-2023  润新知