Flask 应用程序退出时如何停止 Gunicorn

Posted

技术标签:

【中文标题】Flask 应用程序退出时如何停止 Gunicorn【英文标题】:How to stop Gunicorn when Flask application exits 【发布时间】:2016-12-10 01:31:49 【问题描述】:

我正在使用 gunicorn 来运行我的 Flask 应用程序,但是当 Flask 应用程序由于错误而退出时,gunicorn 将创建一个新的 worker 并且不会退出。

示例 Flask 应用程序:

$ vim app.py

# main file
import sys
import os
import logging
from flask import Flask

from views import views

def create_app():

    app = Flask(__name__)

    app_name = os.environ.get('FLASK_APP_NAME', None)
    if app_name is None:
        logging.error("Failed to load configuration")
        sys.exit(2)

    app.config['APP_NAME'] = app_name

    console = logging.StreamHandler(sys.stdout)
    logging.getLogger().addHandler(console)
    logging.getLogger().setLevel(logging.INFO)

    logging.info("Starting Flask application")

    app.register_blueprint(views)

    return app

app = create_app()

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=8000, debug=False, threaded=True)


$ vim views.py

# views
from flask import request, jsonify, Blueprint
from flask import current_app as app

views = Blueprint('views', __name__)

@views.route("/", methods=["GET"])
def indexz():
    return jsonify(app=app.config['APP_NAME'], msg='OK'), 200

我尝试使用 gunicorn 的 worker_exit 服务器挂钩:

$ vim gunicorn-config.py

import sys

workers = 2

def worker_exit(server, worker):
    sys.exit(2)
    # server.halt()

但这只会引发错误并继续产生工人:

# error when using sys.exit(2)
[2016-12-10 01:28:53 +0000] [11916] [INFO] Booting worker with pid: 11916
ERROR:root:Failed to load configuration
[2016-12-10 01:28:53 +0000] [11915] [INFO] Worker exiting (pid: 11915)
[2016-12-10 01:28:53 +0000] [11915] [WARNING] Exception during worker exit:
Traceback (most recent call last):
  File "/usr/lib/python2.7/site-packages/gunicorn/arbiter.py", line 576, in spawn_worker
    self.cfg.worker_exit(self, worker)
  File "gunicorn-config.py", line 6, in worker_exit
    sys.exit(2)
SystemExit: 2


# error when using server.halt()
[2016-12-10 01:30:15 +0000] [12202] [INFO] Booting worker with pid: 12202
ERROR:root:Failed to load configuration
[2016-12-10 01:30:15 +0000] [12197] [INFO] Worker exiting (pid: 12197)
[2016-12-10 01:30:15 +0000] [12197] [INFO] Shutting down: Master
[2016-12-10 01:30:15 +0000] [12197] [WARNING] Exception during worker exit:
Traceback (most recent call last):
  File "/usr/lib/python2.7/site-packages/gunicorn/arbiter.py", line 576, in spawn_worker
    self.cfg.worker_exit(self, worker)
  File "gunicorn-config.py", line 7, in worker_exit
    server.halt()
  File "/usr/lib/python2.7/site-packages/gunicorn/arbiter.py", line 331, in halt
    self.stop()
  File "/usr/lib/python2.7/site-packages/gunicorn/arbiter.py", line 378, in stop
    self.kill_workers(sig)
  File "/usr/lib/python2.7/site-packages/gunicorn/arbiter.py", line 600, in kill_workers
    self.kill_worker(pid, sig)
  File "/usr/lib/python2.7/site-packages/gunicorn/arbiter.py", line 616, in kill_worker
    self.cfg.worker_exit(self, worker)
  File "gunicorn-config.py", line 7, in worker_exit
    server.halt()
  File "/usr/lib/python2.7/site-packages/gunicorn/arbiter.py", line 338, in halt
    sys.exit(exit_status)
SystemExit: 0

编辑:根据https://github.com/benoitc/gunicorn/blob/master/gunicorn/arbiter.py#L37,将我的应用程序的退出代码修改为:sys.exit(4)。这次应用程序不会无限重启,但是当有 2 个或更多工作人员时,会引发异常:

[2016-12-10 18:45:52 +0000] [22195] [INFO] Worker exiting (pid: 22195)
Traceback (most recent call last):
  File "/usr/bin/gunicorn", line 11, in <module>
    sys.exit(run())
  File "/usr/lib/python2.7/site-packages/gunicorn/app/wsgiapp.py", line 74, in run
    WSGIApplication("%(prog)s [OPTIONS] [APP_MODULE]").run()
  File "/usr/lib/python2.7/site-packages/gunicorn/app/base.py", line 192, in run
    super(Application, self).run()
  File "/usr/lib/python2.7/site-packages/gunicorn/app/base.py", line 72, in run
    Arbiter(self).run()
  File "/usr/lib/python2.7/site-packages/gunicorn/arbiter.py", line 218, in run
    self.halt(reason=inst.reason, exit_status=inst.exit_status)
  File "/usr/lib/python2.7/site-packages/gunicorn/arbiter.py", line 331, in halt
    self.stop()
  File "/usr/lib/python2.7/site-packages/gunicorn/arbiter.py", line 381, in stop
    time.sleep(0.1)
  File "/usr/lib/python2.7/site-packages/gunicorn/arbiter.py", line 231, in handle_chld
    self.reap_workers()
  File "/usr/lib/python2.7/site-packages/gunicorn/arbiter.py", line 506, in reap_workers
    raise HaltServer(reason, self.WORKER_BOOT_ERROR)
gunicorn.errors.HaltServer: <HaltServer 'Worker failed to boot.' 3>

如何让应用程序退出而不触发任何异常。

【问题讨论】:

您是否尝试过创建 Try-Except 块来捕获异常并在 except 块中杀死 gunicorn? 我应该把这个 try/except 块放在哪里? 【参考方案1】:

这退出 gunicorn:

sys.exit(4)

【讨论】:

天哪,谢谢!这正是我想要的♥(虽然我建议sys.exit(4) 根据@RobinWinslow 的建议将os._exit(4) 更改为sys.exit(4)。它似乎也同样有效。 @personal_cloud,您能否在文档中添加一个链接来证明为什么使用代码 0 的标准 sys.exit() 是不够的?谢谢! @rite2hhh 根据我的经验,sys.exit() 退出了工作进程,而不是主进程。如果工人退出,gunicorn 会生成一个新的。【参考方案2】:

为了完整起见,请使用标准错误代码: https://docs.python.org/3/library/errno.html#errno.EINTR

所以:

import sys, errno
sys.exit(errno.EINTR)

【讨论】:

【参考方案3】:

查看gunicorn 配置中的preload_app 选项。这会将您的应用程序加载到父级中。然后它应该能够引发异常并且gunicorn 不会启动。

【讨论】:

预加载不是我可以在我的应用程序中使用的选项 使用预加载选项有什么问题? 即使在创建了 preload worker 之后。【参考方案4】:

我正在使用(在 Gunicorn 配置文件中)...

server.halt(reason="DB Connection failed.", exit_status=4)

Sintaxis 在这里:https://github.com/benoitc/gunicorn/blob/1299ea9e967a61ae2edebe191082fd169b864c64/gunicorn/arbiter.py#L340

还有,这里是记录器:

[2021-07-08 17:16:09 -0400] [44647] [INFO] 原因:数据库连接失败。 [2021-07-08 17:16:09 -0400] [44647] [INFO] 大师 进程退出...

最后一行是从“on_exit()”生成的...

def on_exit(server):
    ...
    server.log.info("Master process exiting...")
    ...

【讨论】:

我可以知道您从哪里调用server.halt,即您从哪个函数调用halt 方法或者您如何访问server 对象?

以上是关于Flask 应用程序退出时如何停止 Gunicorn的主要内容,如果未能解决你的问题,请参考以下文章

使用 python flask 流式传输时查看进程未停止

Flask + Nginx + Gunicorn + Gevent部署

LabVIEW实现应用程序停止或退出

如何检查条件,如果条件为真,则停止报价器并退出应用程序,如果条件不成立并且发生超时,则退出应用程序

让我的 GPS 传感器停止时遇到一些问题

如何在应用退出后停止显示插页式广告?