I Return an immedite response and make the process

2020-07-26 09:25发布

I have a process from 10 seconds, but after the 10 seconds, I don't need to return the information to the user but write it down on a dynamoDB, that's why I would like the user doesn't have to wait 10 seconds. Instead, I would like an immediate "Success" response after the post request.

I read a couple of posts and in this one, the answer was with a Teardown Callback, but there wasn't an example.

I read then this, but it doesn't help me with my problem.

I read of course teardown-callbacks and this pattern but I don't know how I could use it another way.

My code looks like this:

@app.route('/ocr/read_image', methods=['POST'])
def get_text():    
    return jsonify('Success')

@app.teardown_request
def teardown_request(response):        
    time.sleep(10)

It actually returns the "Success" Message but just after the 10 seconds.

Is there a way to return the "Succes" Message before the 10 seconds?

I've been reading that it's maybe possible with celery but I would love avoid it if I can.

Does anyone know how to do it?

标签: python flask
3条回答
看我几分像从前
2楼-- · 2020-07-26 09:53

As Ardaglio said, the best way was using multithreading.

I didn't use Celery, because I think it's pretty complicated and my problem is quite easy for it.

So, I'm using Thread:

from threading import Thread

@app.route('/ocr/read_image', methods=['POST'])
def get_text():    
    Thread(target=continue_processing).start()
    return jsonify('Success')

def continue_processing():
    time.sleep(10)
    print('Hi')

But, you gotta be careful. I'm using Keras with Tensorflow as Backend, and if you use it so, you will have a nice value error ValueError: Tensor Tensor()is not an element of this graph.

So, to avoid it inside a Thread, you've to save the Graph after the model is made:

GRAPH = tf.get_default_graph()

and then you've to use it inside the asynchron process this way:

with GRAPH.as_default():
    do something with your model 

Hope it could be help someone.

查看更多
闹够了就滚
3楼-- · 2020-07-26 09:59

Celery can get your work done.

You need to perform some async process. It means exactly what you want: call 'someone' to process your information, then send a message to your user/application without waiting the return of the processed information.

Then, you can add a callback to notify something else when you finished processing it.

All that can be done with Celery or RabbitMQ.

查看更多
够拽才男人
4楼-- · 2020-07-26 10:00

Threading complicates things unnecessarily here as well imho. To do what you're looking to do without introducing threads you can hook into the WSGI response close method. The simplest way to do this is by using the werkzeug ClosingIterator helper.

import traceback
from werkzeug.wsgi import ClosingIterator

class AfterThisResponse:
    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 extensioe
        app.after_this_response = self

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

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

class AfterThisResponseMiddleware:
    def __init__(self, application, after_this_response_ext):
        self.application = application
        self.after_this_response_ext = after_this_response_ext

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

You then use this extension like this:

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

@app.route("/")
def home():
    @app.after_this_response
    def post_process():
        time.sleep(2)
        print("after_response")

    return "Success!\n"

When you curl you will see an immediate success and then 2s later in your logs you will see your "after_response" message:

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

This solution is a summary adapted from my answers:

查看更多
登录 后发表回答