Flask返回响应后执行一个函数(Execute a function after Flask returns response)

Flask返回响应需要执行一些代码。 我认为它不够复杂,无法像Celery那样为它设置一个任务队列。 关键要求是Flask必须在运行此函数之前将响应返回给客户端。 它不能等待函数执行。

关于这一点存在一些现有的问题,但是没有一个答案似乎解决了在将响应发送到客户端之后运行任务,它们仍然同步执行然后返回响应。

在Flask中返回响应后需要执行一个函数 Python Flask立即发送响应 在Flask中返回响应后需要执行一个函数 Flask结束响应并继续处理

I have some code that needs to execute after Flask returns a response. I don't think it's complex enough to set up a task queue like Celery for it. The key requirement is that Flask must return the response to the client before running this function. It can't wait for the function to execute.

There are some existing questions about this, but none of the answers seem to address running a task after the response is sent to the client, they still execute synchronously and then the response is returned.

Need to execute a function after returning the response in Flask Python Flask sending response immediately Need to execute a function after returning the response in Flask Flask end response and continue processing

最满意答案

长话短说,Flask没有提供任何特殊功能来实现这一点。 对于简单的一次性任务,请考虑Python的多线程,如下所示。 对于更复杂的配置,请使用RQ或Celery等任务队列。

为什么?

理解Flask提供的功能以及为什么他们无法实现预期的目标是很重要的。 所有这些在其他情况下都很有用,并且很好的阅读,但是对后台任务不起作用。

Flask的after_request处理程序

Flask的after_request处理程序(在此模式中对延迟请求回调以及此附加的每个请求附加不同函数的代码片段详细介绍)将会将请求传递给回调函数。 预期的用例是修改请求 ,比如附加一个cookie。

因此,请求将等待这些处理程序完成执行,因为期望是请求本身将因此而改变。

Flask的teardown_request处理程序

这与after_request类似,但teardown_request未收到request对象。 这意味着它不会等待请求,对吧?

这看起来像解决方案,因为这个类似堆栈溢出问题的答案建议。 而且由于Flask的文档解释了拆卸回调独立于实际的请求并且没有收到请求上下文,所以你有充分理由相信这一点。

不幸的是, teardown_request仍然是同步的,它只是在请求不再可修改时在Flask的请求处理的后期部分发生。 在返回响应之前,Flask 仍将等待拆卸功能完成,因为这个Flask回调和错误列表指示。

Flask的流式响应

Flask可以通过传递一个生成器给Response()来传递Response() ,因为这个Stack Overflow回答了类似的问题 。

通过流式传输,客户端在请求结束之前确实开始接收响应。 但是,该请求仍然同步运行,因此处理该请求的工作人员处于忙碌状态,直到流完成。

这种用于流式传输的Flask模式包括一些使用stream_with_context()文档,这是包含请求上下文所必需的。

那么解决方案是什么?

Flask没有提供在后台运行功能的解决方案,因为这不是Flask的责任。

在大多数情况下,解决此问题的最佳方法是使用RQ或Celery等任务队列。 这些处理棘手的事情,比如配置,调度和为你分发工作人员。这是这类问题最常见的答案,因为它是最正确的,并且迫使你以考虑上下文等方式设置事物。正确。

如果你需要在后台运行一个函数,并且不想设置一个队列来管理它,你可以使用Python内置的threading或多multiprocessing来产生一个后台工作器。

您无法从后台任务访问Flask线程本地的request或其他人,因为请求在此处不会被激活。 相反,当你创建它时,将你需要的数据从视图传递到后台线程。

@app.route('/start_task')
def start_task():
    def do_work(value):
        # do something that takes a long time
        import time
        time.sleep(20)

    thread = Thread(target=do_work, kwargs={'value': request.args.get('value', 20))
    thread.start()
    return 'started'

The long story short is that Flask does not provide any special capabilities to accomplish this. For simple one-off tasks, consider Python's multithreading as shown below. For more complex configurations, use a task queue like RQ or Celery.

Why?

It's important to understand the functions Flask provides and why they do not accomplish the intended goal. All of these are useful in other cases and are good reading, but don't help with background tasks.

Flask's after_request handler

Flask's after_request handler, as detailed in this pattern for deferred request callbacks and this snippet on attaching different functions per request, will pass the request to the callback function. The intended use case is to modify the request, such as to attach a cookie.

Thus the request will wait around for these handlers to finish executing because the expectation is that the request itself will change as a result.

Flask's teardown_request handler

This is similar to after_request, but teardown_request doesn't receive the request object. So that means it won't wait for the request, right?

This seems like the solution, as this answer to a similar Stack Overflow question suggests. And since Flask's documentation explains that teardown callbacks are independent of the actual request and do not receive the request context, you'd have good reason to believe this.

Unfortunately, teardown_request is still synchronous, it just happens at a later part of Flask's request handling when the request is no longer modifiable. Flask will still wait for teardown functions to complete before returning the response, as this list of Flask callbacks and errors dictates.

Flask's streaming responses

Flask can stream responses by passing a generator to Response(), as this Stack Overflow answer to a similar question suggests.

With streaming, the client does begin receiving the response before the request concludes. However, the request still runs synchronously, so the worker handling the request is busy until the stream is finished.

This Flask pattern for streaming includes some documentation on using stream_with_context(), which is necessary to include the request context.

So what's the solution?

Flask doesn't offer a solution to run functions in the background because this isn't Flask's responsibility.

In most cases, the best way to solve this problem is to use a task queue such as RQ or Celery. These manage tricky things like configuration, scheduling, and distributing workers for you.This is the most common answer to this type of question because it is the most correct, and forces you to set things up in a way where you consider context, etc. correctly.

If you need to run a function in the background and don't want to set up a queue to manage this, you can use Python's built in threading or multiprocessing to spawn a background worker.

You can't access request or others of Flask's thread locals from background tasks, since the request will not be active there. Instead, pass the data you need from the view to the background thread when you create it.

@app.route('/start_task')
def start_task():
    def do_work(value):
        # do something that takes a long time
        import time
        time.sleep(value)

    thread = Thread(target=do_work, kwargs={'value': request.args.get('value', 20))
    thread.start()
    return 'started'

                    
                     
          

更多推荐