【问题标题】:Python Tornado Async Fetching of URLsPython Tornado 异步获取 URL
【发布时间】:2016-05-12 21:15:01
【问题描述】:

在下面的代码示例中,我有一个函数do_async_thing,它似乎返回一个Future,尽管我不确定为什么?

import tornado.ioloop
import tornado.web
import tornado.httpclient

@tornado.gen.coroutine
def do_async_thing():
    http = tornado.httpclient.AsyncHTTPClient()
    response = yield http.fetch("http://www.google.com/")
    return response.body

class MainHandler(tornado.web.RequestHandler):
    def get(self):
        x = do_async_thing()
        print(x) # <tornado.concurrent.Future object at 0x10753a6a0>

        self.set_header("Content-Type", "application/json")
        self.write('{"foo":"bar"}')
        self.finish()

if __name__ == "__main__":
    app = tornado.web.Application([
        (r"/foo/?", MainHandler),
    ])
    app.listen(8888)

    tornado.ioloop.IOLoop.current().start()

你会看到我yield 调用了fetch,这样做我应该强制该值实现(随后能够访问body 字段的响应)。

更有趣的是我什至可以访问 Future 上的 body 字段并且不会出错(据我所知,Future 没有这样的字段/属性/方法)

那么有谁知道我该怎么做:

  1. 解决未来,让我得到实际价值
  2. 修改此示例,使函数 do_async_thing 进行多个异步 url 获取

现在值得注意的是,因为我仍然得到 Future 回来,所以我想我会尝试添加一个 yield 来为对 do_async_thing() 的调用添加前缀(例如 x = yield do_async_thing()),但这给了我以下错误:

tornado.gen.BadYieldError: yielded unknown object <generator object get at 0x1023bc308>

第二点我也考虑过这样做:

def do_another_async_thing():
    http = tornado.httpclient.AsyncHTTPClient()
    a = http.fetch("http://www.google.com/")
    b = http.fetch("http://www.github.com/")
    return a, b

class MainHandler(tornado.web.RequestHandler):
    def get(self):
        y = do_another_async_thing()
        print(y)

但这又会返回:

<tornado.concurrent.Future object at 0x102b966d8>

至少我会期望一个 Futures 元组在哪里?在这一点上,我无法解决这些期货,而不会出现以下错误:

tornado.gen.BadYieldError: yielded unknown object <generator object get at 0x1091ac360>

更新

以下是一个有效的示例(根据 A. Jesse Jiryu Davis 的回答)

但我还添加了另一个示例,其中我有一个新函数 do_another_async_thing,它使 两个 异步 HTTP 请求(但评估它们的值会更复杂一点,正如您将看到的那样):

def do_another_async_thing():
    http = tornado.httpclient.AsyncHTTPClient()
    a = http.fetch("http://www.google.com/")
    b = http.fetch("http://www.github.com/")
    return a, b

@tornado.gen.coroutine
def do_async_thing():
    http = tornado.httpclient.AsyncHTTPClient()
    response = yield http.fetch("http://www.google.com/")
    return response.body

class MainHandler(tornado.web.RequestHandler):
    @tornado.gen.coroutine
    def get(self):
        x = yield do_async_thing()
        print(x) # displays HTML response

        fa, fb = do_another_async_thing()
        fa = yield fa
        fb = yield fb
        print(fa.body, fb.body) # displays HTML response for each

值得澄清:您可能认为do_another_async_thing 的两个yield 语句会导致阻塞。但这里是正在发生的步骤的细分:

  • do_another_async_thing 立即返回一个包含两个 Future 的元组
  • 我们yield第一个导致程序阻塞直到值实现的元组
  • 值实现了,所以我们转到下一行
  • 我们再次yield,导致程序阻塞,直到值实现
  • 但由于两个期货同时创建并同时运行,第二个 yield 几乎立即返回

【问题讨论】:

    标签: python concurrency tornado


    【解决方案1】:

    协程返回期货。要等待协程完成,调用者必须也是是协程,并且必须让出未来。所以:

    @gen.coroutine
    def get(self):
        x = yield do_async_thing()
    

    欲了解更多信息,请参阅Refactoring Tornado Coroutines

    【讨论】:

    • 谢谢,这似乎有效。您将如何构造代码以使do_async_thing 实际上对http.fetch 进行了两次单独的调用?我假设因为yield 强制对值进行评估,那么如果不否定异步性能,您将无法执行我的要求。相反,也许您会有另一个标有@tornado.gen.coroutine 的函数来异步获取另一个url?或者,也许您没有产生多个提取,而是在函数末尾产生了一个元组;但是在某些时候get(self) 需要yield 两次(对于每个在元组中返回的 Future 一次)
    • 我链接的文章也回答了这个确切的问题。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-07-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多