Flask中有没有办法将响应发送给客户端,然后继续进行一些处理?我有几个要完成的簿记任务,但我不想让客户等待。
请注意,这些实际上是我想要做的很快的事情,因此创建新线程或使用队列在这里并不合适。 (这些快速的事情之一实际上是在作业队列中添加了一些内容。)
答案 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