Flask结束响应并继续处理

时间:2013-06-25 09:09:05

标签: python flask

Flask中有没有办法将响应发送给客户端,然后继续进行一些处理?我有几个要完成的簿记任务,但我不想让客户等待。

请注意,这些实际上是我想要做的很快的事情,因此创建新线程或使用队列在这里并不合适。 (这些快速的事情之一实际上是在作业队列中添加了一些内容。)

6 个答案:

答案 0 :(得分:5)

快速 EASY 方法。

我们将使用pythons Thread 库来实现这一目标。

您的API使用者发送了一些要处理的东西,并由 my_task() 函数处理,该过程需要 10秒 em>执行。 但是,API的使用者在点击您的 return_status() 函数之类的API时就希望得到响应。

您将 my_task tie 绑定到 thread em>,然后将快速响应返回给API使用者,而在后台,大型流程变得复杂了。

下面是一个简单的POC。

import os
from flask import Flask,jsonify
import time
from threading import Thread

app = Flask(__name__)

@app.route("/")
def main():
    return "Welcome!"

@app.route('/add_')
def return_status():
    """Return first the response and tie the my_task to a thread"""
    Thread(target = my_task).start()
    return jsonify('Response asynchronosly')

def my_task():
    """Big function doing some job here I just put pandas dataframe to csv conversion"""
    time.sleep(10)
    import pandas as pd
    pd.DataFrame(['sameple data']).to_csv('./success.csv')
    return print('large function completed')

if __name__ == "__main__":
    app.run(host="0.0.0.0", port=8080)

答案 1 :(得分:4)

听起来像Teardown Callbacks会支持你想要的东西。您可能希望将其与Per-Request After-Request Callbacks中的模式结合起来,以帮助组织代码。

答案 2 :(得分:2)

我的博客遇到了类似的问题。我希望在发布新评论时向那些订阅评论的人发送通知电子邮件,但我不希望发布评论的人等待所有电子邮件在收到回复之前发送。

我为此使用了multiprocessing.Pool。我创建了一个工作池(这是足够的,流量较低的站点),然后每次我需要发送电子邮件时,我在Flask视图功能中准备好所有内容,但是通过{将最终send_email调用传递给池{1}}。

答案 3 :(得分:1)

您可以在Flask中找到有关如何使用芹菜的示例 这里https://gist.github.com/jzempel/3201722

这个想法的主旨(双关语)是将长期的簿记任务定义为@ celery.task并使用apply_async 1或延迟从视图中开始任务

答案 4 :(得分:1)

将响应返回给客户端后,无法执行令人沮丧的拆卸回调:

import flask
import time
app = flask.Flask("after_response")

@app.teardown_request
def teardown(request):
    time.sleep(2)
    print("teardown_request")

@app.route("/")
def home():
    return "Success!\n"

if __name__ == "__main__":
    app.run()

卷曲时,您会注意到在显示响应之前有2s的延迟,而不是卷曲立即结束,然后在2s之后记录。日志进一步确认了这一点:

teardown_request
127.0.0.1 - - [25/Jun/2018 15:41:51] "GET / HTTP/1.1" 200 -

返回响应后执行的正确方法是使用WSGI中间件,该中间件向close method of the response iterator添加了一个钩子。这不像teardown_request装饰器那样简单,但是仍然很简单:

import traceback
from werkzeug.wsgi import ClosingIterator

class AfterResponse:
    def __init__(self, app=None):
        self.callbacks = []
        if app:
            self.init_app(app)

    def __call__(self, callback):
        self.callbacks.append(callback)
        return callback

    def init_app(self, app):
        # install extension
        app.after_response = self

        # install middleware
        app.wsgi_app = AfterResponseMiddleware(app.wsgi_app, self)

    def flush(self): for fn in self.callbacks:
            try:
                fn()
            except Exception:
                traceback.print_exc()

class AfterResponseMiddleware:
    def __init__(self, application, after_response_ext):
        self.application = application
        self.after_response_ext = after_response_ext

    def __call__(self, environ, start_response):
        iterator = self.application(environ, start_response)
        try:
            return ClosingIterator(iterator, [self.after_response_ext.flush])
        except Exception:
            traceback.print_exc()
            return iterator

然后您可以像这样使用:

@app.after_response
def after():
    time.sleep(2)
    print("after_response")

从外壳中,您将立即看到响应返回,然后2秒后after_response将进入日志:

127.0.0.1 - - [25/Jun/2018 15:41:51] "GET / HTTP/1.1" 200 -
after_response

这是here提供的先前答案的总结。

答案 5 :(得分:0)

您可以使用由Werkzeug Response对象的curl -X GET 'https://api.twilio.com/2010-04-01/Accounts/ACXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX/IncomingPhoneNumbers.json?PageSize=20' \ -u ACXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX:your_auth_token 装饰器公开的WSGI close协议来完成此操作。此处在此其他答案中作了解释:https://stackoverflow.com/a/63080968/78903