Завершение ответа колбы и продолжение обработки

Есть ли в 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

Другие вопросы по тегам