Завершение ответа колбы и продолжение обработки
Есть ли в Flask способ отправить ответ клиенту и продолжить обработку? У меня есть несколько задач по бухгалтерии, которые необходимо выполнить, но я не хочу заставлять клиента ждать.
Обратите внимание, что это действительно очень быстрые вещи, которые я хочу сделать, поэтому создание нового потока или использование очереди здесь не совсем уместно. (Одна из этих быстрых вещей - это добавление чего-либо в очередь заданий.)
4 ответа
Похоже, обратные вызовы Teardown будут поддерживать то, что вы хотите. И вы можете захотеть объединить его с шаблоном из обратных вызовов по запросу после запроса, чтобы помочь с организацией кода.
БЫСТРЫЙ иЛЕГКИЙ метод.
Мы будем использовать библиотеку потоков pythons, чтобы добиться этого.
Ваш потребитель API отправил что-то для обработки, и это обрабатывается функцией my_task(), выполнение которой занимает 10 секунд. Но потребитель API хочет получить ответ, как только он попадет в ваш API, который является функцией return_status().
Вы привязать к my_task к нити, а затем вернуть быстрый ответ на API потребителя, в то время как в фоновом режиме большой процесс получает compelete.
Ниже представлен простой 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)
К сожалению, обратные вызовы разрыва не выполняются после того, как ответ был возвращен клиенту:
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()
При свертывании этого параметра вы заметите задержку в 2 секунды перед отображением ответа, а не завершение завитка сразу, а затем журнал через 2 секунды. Это также подтверждается журналами:
teardown_request
127.0.0.1 - - [25/Jun/2018 15:41:51] "GET / HTTP/1.1" 200 -
Правильный способ выполнения после возврата ответа - использовать промежуточное программное обеспечение WSGI, которое добавляет хук к методу close итератора ответа. Это не так просто, как 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
Это краткое изложение предыдущего ответа, приведенного здесь.
У меня была похожая проблема с моим блогом. Я хотел отправлять уведомления по электронной почте тем, кто подписан на комментарии, когда был опубликован новый комментарий, но я не хотел, чтобы человек, публикующий комментарий, ждал, пока все электронные письма будут отправлены, прежде чем он получит свой ответ.
Я использовал multiprocessing.Pool
за это. Я запустил пул одного работника (этого было достаточно, сайт с низким трафиком), а затем каждый раз, когда мне нужно отправить электронное письмо, я готовлю все в функции просмотра Flask, но передаю окончательный вариант send_email
позвонить в бассейн через apply_async
,
Вы можете найти пример того, как использовать сельдерей из Flask здесь https://gist.github.com/jzempel/3201722
Суть идеи (каламбур) состоит в том, чтобы определить длинные задачи бухгалтерского учета как @celery.task и использовать apply_async 1 или задержку изнутри представления, чтобы запустить задачу
Вы можете сделать это с помощью WSGI close
протокол, предоставляемый объектом Werkzeug Response call_on_close
декоратор. Объясняется в этом другом ответе здесь: /questions/3409183/vyipolnit-funktsiyu-posle-togo-kak-flask-vernet-otvet/55349165#55349165