简体   繁体   English

如何在Flask中运行后台计时器

[英]How to run a background timer in Flask

I'm trying to write a Flask app that behaves correctly when running in Kubernetes , especially when it comes to graceful shutdown . 我正在尝试编写一个Flask应用程序,该应用程序在Kubernetes中运行时尤其是在正常关机时运行正常

As such, I need to have the code: 因此,我需要代码:

  • recieve the shutdown signal 收到关机信号
  • start the "shutdown timer" 启动“关机计时器”
  • continue serving requests as normal until the time is "up" 继续正常处理请求,直到时间到了
  • then shut itself down 然后关闭自己

So far, what I've got is this: 到目前为止,我所得到的是:

from flask import Flask, abort
import signal
import time
app = Flask(__name__)
shuttingDown = False

def exit_gracefully(self, signum):
    app.logger.error('Received shutdown signal. Exiting gracefully')
    global shuttingDown
    shuttingDown = True
    # TODO: wait for some time here to ensure we are not receiving any more
    # traffic
    time.sleep(20)
    exit(0)

signal.signal(signal.SIGTERM, exit_gracefully)

@app.route("/")
def hello():
        return "Hello World!"

@app.route("/_status/liveness")
def liveness():
        return "I am alive"

@app.route("/_status/readiness")
def readiness():
        if not shuttingDown:
            return "I am ready"
        else:
            abort(500, 'not ready anymore')

The above "works" except after I send the "shutdown" signal, regular requests to "/" are not responded to, even though we are still within the shutdown "grace period" (20 seconds in the above code). 除了我发送“ shutdown”信号后,上述“工作”正常,对“ /”的常规请求均未响应,即使我们仍处于关闭“宽限期”(上述代码中为20秒)内。

It seems that the call to "time.sleep()" is synchronous. 似乎对“ time.sleep()”的调用是同步的。

Does anyone know of any way to make this "asynchronous"? 有谁知道使这种“异步”的方法吗? So that the app continues to service requests until the time is "up"? 这样,该应用就可以继续处理请求,直到时间到了为止。

At a guess you are using the builtin Flask development server. 猜测您正在使用内置的Flask开发服务器。 Such builtin development servers in Flask and Django, or anything based on the web server or WSGI server in the standard library are not intended for production systems and don't normally handle shutdown on signals properly. Flask和Django中的此类内置开发服务器,或标准库中基于Web服务器或WSGI服务器的任何内容,均不适用于生产系统,并且通常无法正确处理信号的关闭。

As such, you should really use a proper production grade WSGI server such as Apache/mod_wsgi (mod_wsgi-express), gunicorn or uWSGI. 因此,您实际上应该使用适当的生产级WSGI服务器,例如Apache / mod_wsgi(mod_wsgi-express),gunicorn或uWSGI。 These all handle signals properly and you don't have the issue with the development servers where they ignore the signal causing the container shutdown to be delayed, with it eventually being killed by Kubernetes when the shutdown timeout occurs. 这些都正确地处理了信号,并且开发服务器没有问题,它们忽略了导致容器关闭延迟的信号,当关闭超时发生时,它最终被Kubernetes杀死。

This works with the internal server. 这适用于内部服务器。 The caveat is there is a /_shutdown URL that shuts the server down, and this is open to malicious shutdowns. 需要注意的是,有一个/ _shutdown URL可以关闭服务器,这对恶意关闭很开放。 If this is not what you want, then remove requests.post() and uncomment os._exit() . 如果这不是你想要的,然后取出requests.post()并取消os._exit() And of course remove @app.route("/_shutdown") and the function as well. 当然, @app.route("/_shutdown")删除@app.route("/_shutdown")和该函数。

from flask import Flask, abort, request
import signal
import threading
import time
import os
import requests
app = Flask(__name__)
shuttingDown = False


def exit_call():
    time.sleep(20)
    requests.post("http://localhost:5420/_shutdown")
    # os._exit(0)


def exit_gracefully(self, signum):
    app.logger.error('Received shutdown signal. Exiting gracefully')
    global shuttingDown
    shuttingDown = True
    # TODO: wait for some time here to ensure we are not receiving any more
    # traffic
    _et = threading.Thread(target=exit_call)
    _et.daemon = True
    _et.start()


signal.signal(signal.SIGTERM, exit_gracefully)


@app.route("/")
def hello():
        return "Hello World!"


@app.route("/_status/liveness")
def liveness():
        return "I am alive"


@app.route("/_shutdown", methods=["POST"])
def shutdown():
    func = request.environ.get('werkzeug.server.shutdown')
    if func is None:
        return "Not a werkzeug server"
    func()
    return "shutdown"


@app.route("/_status/readiness")
def readiness():
        if not shuttingDown:
            return "I am ready"
        else:
            abort(500, 'not ready anymore')


app.run(port=5420)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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