如何在gunicorn应用程序中正确处理SIGTERM?

问题描述 投票:0回答:1

我有一个 Python Flask 应用程序:

from flask import Flask, Response
import signal

app = Flask("myapp")

def exit_gracefully(*args):
    print("Doing something here")

signal.signal(signal.SIGINT, exit_gracefully)
signal.signal(signal.SIGTERM, exit_gracefully)

@app.route("/myendpoint")
def myendpoint():
    print("Doing something here")
    return Response("Everything is fine", status=200, mimetype="application/json")

这个应用程序由gunicorn运行,使用经典的

wsgi.py
文件和systemd服务:

[Unit]
Description=myservice

[Service]
User=root
Group=root
WorkingDirectory=/opt/myservice
ExecStart=/opt/myservice/run.sh

[Install]
WantedBy=multi-user.target
#!/bin/bash
source env/bin/activate
env/bin/gunicorn --bind unix:gunicorn.sock --workers 1 --timeout 10 wsgi:app

我需要拦截发送到服务的SIGTERM信号,因为我需要执行一些清理和中断(特别是设置

threading.Event()
标志)

但是,问题是,当我输入

systemctl stop myservice
时,服务会在关闭前挂起 30 秒(这可能是gunicorn 的
graceful_timeout
设置),而不是像我打算的那样立即关闭。

在查看gunicorn的源代码时,我注意到Worker类使用信号处理程序来优雅地关闭自身:

# gunicorn/workers/base.py:173
signal.signal(signal.SIGTERM, self.handle_exit)

我的猜测是,由于每个信号只能使用一个处理程序,因此我将使用我自己的处理程序覆盖该处理程序。 但我仍然需要两个处理程序,我的处理程序用于完成清理操作,默认的处理程序用于让 Gunicorn 工作人员正确停止。

也许值得一提的是,当我在 shell 中运行

run.sh
并向其发送 SIGINT(按 Ctrl+C)时,我的应用程序立即停止,尽管 Gunicorn 也为其实现了一个处理程序。

关于我应该如何进行有什么想法吗?我想过在我自己的处理程序中调用函数

handle_exit
,但由于我的代码是“gunicorn-agnostic”,我不知道应该如何做到这一点。我应该更改应用程序的设计吗?

python signals gunicorn sigterm
1个回答
0
投票

我也有同样的问题。当收到

SIGTERM
时,我的工作人员不会停止执行。就我而言,我正在与 Flask 应用程序一起运行一个线程。我正在接收信号并成功停止线程,但gunicorn并没有停止。解决方案是在
exit(0)
之后添加
thread.join()
语句。

我假设如果没有退出代码,gunicorn 会以某种方式认为该进程仍然存在。

from flask import Flask, Response
import signal
import time
import threading

app = Flask("myapp")

@app.route("/myendpoint")
def myendpoint():
    print("Doing something here")
    return Response("Everything is fine", status=200, mimetype="application/json")

must_stop = False
def sleppy_thread():
    while True:
        time.sleep(0.1)
        if must_stop:
            break

def exit_gracefully(*args):
    global must_stop
    must_stop = True

signal.signal(signal.SIGINT, exit_gracefully)
signal.signal(signal.SIGTERM, exit_gracefully)

sleepy_thread = threading.Thread(target=sleppy_thread)
sleepy_thread.start()
sleepy_thread.join()
exit(0)
© www.soinside.com 2019 - 2024. All rights reserved.