简体   繁体   中英

How to stop Gunicorn when Flask application exits

I am using gunicorn to run my Flask application, however when the Flask application exits because of an error, gunicorn will create a new worker and not exit.

sample Flask application:

$ 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

I tried using gunicorn's worker_exit server hook:

$ vim gunicorn-config.py

import sys

workers = 2

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

But this just throws an error and continues to spawn workers:

# 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

Edit: modified the exit code for my application to: sys.exit(4) as per https://github.com/benoitc/gunicorn/blob/master/gunicorn/arbiter.py#L37 . This time the application does not infinitely restart but when there are 2 or more workers, this throws an exception:

[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>

How can I make the application exit without any exceptions being triggered.

这退出了 gunicorn:

sys.exit(4)

For the sake of completeness use standard errcodes: https://docs.python.org/3/library/errno.html#errno.EINTR

so:

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

Look at the preload_app option in gunicorn configuration. This will load your application in the parent. It should then be able to raise an exception and gunicorn will not start.

I'm using (inside Gunicorn config file) ...

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

Sintaxis is here: https://github.com/benoitc/gunicorn/blob/1299ea9e967a61ae2edebe191082fd169b864c64/gunicorn/arbiter.py#L340

And, here the logger:

[2021-07-08 17:16:09 -0400] [44647] [INFO] Reason: DB connection failed.
[2021-07-08 17:16:09 -0400] [44647] [INFO] Master process exiting...

Last line is generated from " on_exit() "...

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM