如何与Flask异步获取多个资源?

时间:2014-08-12 07:01:02

标签: python asynchronous flask python-requests gevent

我有一个在nginx上通过uwsgi运行的烧瓶应用程序。当用户发出请求时,我需要对不同的服务进行多个API调用。我想同时进行呼叫以加快用户的速度(没有一个呼叫相互依赖)。我还要缓存这些响应以进一步保存。我试图用gevent做这个,但遇到麻烦试图将请求上下文传递给子进程。我收到以下错误:" TypeError:无法连接' str'和' NoneType'对象"

from flask import Flask, request
from flask_cache import Cache
import requests
app = Flask(__name__)
cache = Cache(app=app, config={'CACHE_TYPE': 'filesystem',
            'CACHE_DIR': '/path/to/cache',})


@app.route("/")
def hello():
    def get_external(i, request):
        with app.app_context():
            with app.test_request_context():
                if i == 'first':
                    return first_request()
                elif i == 'second':
                    return second_request()

    threads = [gevent.spawn(get_external, i, request) for i in ['first', 'second']]
    gevent.joinall(threads)
    first = threads[0].get(block=False)
    second = threads[1].get(block=False)
    return render_template('index.html', first=first, second=second)


@cache.cached(timeout=10)
def first_request():
    r = requests.get('http://api.example1.com?' + request.args.get('query'))
    my_list = []
    for row in r.json():
        d = {}
        d['article_id'] = row['article_id']
        my_list.append(d)
    return my_list


@cache.cached(timeout=10000)
def second_request():
    r = requests.get('http://api.example2.com?' + request.args.get('query'))
    my_list = []
    for row in r.json():
        d = {}
        d['id'] = row['user_id']
        my_list.append(d)
    return my_list


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

另外,如果gevent是这项工作的错误工具,那么让我知道。我当然不是python的专家,之前从未使用gevent,但看起来似乎有更简单的方法来做到这一点,不是吗?

编辑:我已尝试使用请求缓存的grequests模块但according to this它不是很好,因为它使用了sqlite作为后端(我还需要改为使用后端文件。)

1 个答案:

答案 0 :(得分:2)

gevent的joinall()是否阻塞,直到所有线程都返回?如果没有,那么我觉得这里缺少一些东西。如果异步启动几个线程,则需要定期检查线程是否全部返回,如果有,则在给定返回值的情况下调用render_template。我可能在这里遗漏了一些东西,因为我还没有使用gevent。

另外,你构建线程的方式对我来说很奇怪...使用列表理解可能不是一个好主意,因为对于每个你必须在get_external()中明确检查它,这似乎很乱。相反,可能更清楚的是为每个线程定义特定的函数,并将每个新线程一次一个地附加到线程。然后您可以明确地引用它们。

缓存响应很容易做到。对于每个函数,跟踪最后返回的值以及何时返回该值。如果cuurent时间 - 上一次检查时间大于某个阈值,则再次检查并更新缓存值并检查时间,否则返回缓存值。如果你想要更强大的东西,你应该看看redis或memcache。