一次击中多个API,龙卷风和python

时间:2017-06-26 23:52:42

标签: python python-2.7 asynchronous tornado coroutine

我正在尝试创建一个API来收集来自其他几个API的响应,并将结果合并到一个响应中。我想异步发送get请求以便它运行得更快,但即使我正在使用协同程序和让步,我的代码似乎仍然是每次请求一个。想知道是否因为我使用请求库而不是龙卷风的AsyncHTTPClient,或者因为我在循环中调用self.path_get,或者因为我将结果存储在实例变量中?

API我正在查看JSON对象的返回数组,我想将它们全部合并到一个数组中并将其写入响应。

from tornado import gen, ioloop, web
from tornado.gen import Return
import requests


PATHS = [
    "http://firsturl",
    "http://secondurl",
    "http://thirdurl"
]


class MyApi(web.RequestHandler):

    @gen.coroutine
    def get(self):
        self.results = []
        for path in PATHS:
            x = yield self.path_get(path)

        self.write({
            "results": self.results,
        })

    @gen.coroutine
    def path_get(self, path):
        resp = yield requests.get(path)
        self.results += resp.json()["results"]
        raise Return(resp)


ROUTES = [
    (r"/search", MyApi),
]


def run():
    app = web.Application(
        ROUTES,
        debug=True,
    )

    app.listen(8000)

    ioloop.IOLoop.current().start()


if __name__ == "__main__":
    run()

1 个答案:

答案 0 :(得分:2)

您的代码无效的原因有很多。首先,requests通常会阻止事件循环,并且不会执行任何其他操作。将requests替换为AsyncHTTPClient.fetch。此外,您产生每个请求的方式也会使请求顺序而不是像您想象的那样同时发生。以下是如何重构代码的示例:

import json
from tornado import gen, httpclient, ioloop, web

# ...

class MyApi(web.RequestHandler):

    @gen.coroutine
    def get(self):
        futures_list = []
        for path in PATHS:
            futures_list.append(self.path_get(path))

        yield futures_list
        result = json.dumps({'results': [x.result() for x in futures_list]})
        self.write(result)

    @gen.coroutine
    def path_get(self, path):
        request = httpclient.AsyncHTTPClient()
        resp = yield request.fetch(path)
        result = json.loads(resp.body.decode('utf-8'))
        raise gen.Return(result)

我们正在创建一个Futures列表,该列表从gen.coroutine函数返回并生成整个列表,直到请求的结果可用。然后,一旦所有请求都完成,就会迭代futures_list,结果将用于创建一个附加到JSON对象的新列表。