【问题标题】:How to wrap asynchronous and gen functions together in Tornado?如何在 Tornado 中将异步和生成函数包装在一起?
【发布时间】:2015-02-13 09:50:52
【问题描述】:

如何在 Tornado 中将异步和生成函数包装在一起? 我的代码如下所示,错误是 'Future' 对象没有属性 'body'。

我是否以错误的方式放置装饰器?

import tornado.httpclient
import tornado.web
import tornado.gen
import tornado.httpserver
import tornado.ioloop

class Class1(tornado.web.RequestHandler):

    @tornado.web.asynchronous
    def post(self, *args, **kwargs):
        url = self.get_argument('url', None)
        response = self.json_fetch('POST', url, self.request.body)
        self.write(response.body)
        self.finish()

    @tornado.gen.engine
    def json_fetch(self, method, url, body=None, *args, **kwargs):
        client = tornado.httpclient.AsyncHTTPClient()
        headers = tornado.httputil.HTTPHeaders({"content-type": "application/json charset=utf-8"})
        request = tornado.httpclient.HTTPRequest(url, method, headers, body)
        yield tornado.gen.Task(client.fetch, request)

【问题讨论】:

    标签: asynchronous generator tornado


    【解决方案1】:

    在此代码示例中您不需要“异步”。 “gen.engine”已过时,请改用“coroutine”。这些天您通常也不需要太多使用“gen.Task”。对您的代码进行四项更改:

    1. 将“post”包装在“coroutine”中
    2. “yield” self.json_fetch 的结果,而不是直接使用结果。
    3. 无需在协程中调用“finish”,Tornado 会在协程完成时完成响应。
    4. 也将 json_fetch 包装在“协程”中。

    结果:

    class ClubCreateActivity(tornado.web.RequestHandler):
    
        @tornado.gen.coroutine
        def post(self, *args, **kwargs):
            url = self.get_argument('url', None)
            response = yield self.json_fetch('POST', url, self.request.body)
            self.write(response.body)
    
        @tornado.gen.coroutine
        def json_fetch(self, method, url, body=None, *args, **kwargs):
            client = tornado.httpclient.AsyncHTTPClient()
            headers = tornado.httputil.HTTPHeaders({"content-type": "application/json charset=utf-8"})
            request = tornado.httpclient.HTTPRequest(url, method, headers, body)
            response = yield client.fetch(request)
            raise gen.Return(response)
    

    进一步阅读:

    【讨论】:

    • 我去捡了gen.coroutine的知识。好像我必须这样写,因为我使用的是 Python 2? raise gen.Return(response.body)
    • 我按上面说的试过了:AttributeError: 'NoneType' object has no attribute 'body'
    • 为什么我还需要在功能帖子中屈服? json_fetch 已经返回了一个 future 对象?
    • 是的,json_fetch 返回一个 Future。要等待 Future 解析响应,“post”函数必须让步。 yield 暂停“post”,直到“json_fetch”完成,然后“post”恢复响应值。
    【解决方案2】:

    tornado 官方文档中推荐的方法是 @tornado.gen.coroutineyield 一起使用。

    如果你想同时使用 asynchronous 和 yield 的优势,你应该嵌套 @tornado.web.asynchronous 装饰器,然后是 @tornado.gen.engine

    【讨论】:

      【解决方案3】:

      关于“异步调用自己的函数”但没有额外的外部回调函数的文档 — Asynchronous and non-Blocking I/O

      你可以让你的 json_fetch 像这样:

      from tornado.concurrent import Future
      
      def json_fetch(self, method, url, body=None, *args, **kwargs):
          http_client = tornado.httpclient.AsyncHTTPClient()
          my_future = Future()
          fetch_future = http_client.fetch(url)
          fetch_future.add_done_callback(
              lambda f: my_future.set_result(f.result()))
          return my_future
      

      或者像这样(来自 A. Jesse Jiryu Davis 的回答):

      from tornado import gen
      
      @gen.coroutine
      def json_fetch(self, method, url, body=None, *args, **kwargs):
          http_client = tornado.httpclient.AsyncHTTPClient()
          headers = tornado.httputil.HTTPHeaders({"content-type": "application/json charset=utf-8"})
          request = tornado.httpclient.HTTPRequest(url, method, headers, body)
          response = yield http_client.fetch(request)
          raise gen.Return(response)
      

      * 在 json_fetch 的“gen.coroutine”和“yield”调用中包装“post”。

      ** "raise gen.Return(response)" 仅适用于 Python2,在 Python3.3 及更高版本中,您应该编写“return response”。

      感谢 A. Jesse Jiryu Davis 提供链接“Tornado 异步请求处理程序”,在那里找到“异步和非阻塞 I/O”。

      【讨论】:

        猜你喜欢
        • 2017-05-18
        • 2020-06-22
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-05-25
        • 1970-01-01
        相关资源
        最近更新 更多