Tornado asynchttpclient 返回 None

1 投票
2 回答
1396 浏览
提问于 2025-04-17 22:31

我卡在这个问题上已经好几个小时了。我想用AsyncHTTPClient来获取一个网站的主页,比如说谷歌。为了实现这个功能,我在同一个类里面定义了一个函数,代码如下:

client = tornado.httpclient.AsyncHTTPClient()

@asynchronous
def getUrlRes(self,url,params,req_type):
    req1 = tornado.httpclient.HTTPRequest(url="http://www.google.com", method="GET")
    self.client.fetch(req1,self.urlCallBack)
    self.finish()

def urlCallBack(self, response):
    print response

我哪里做错了呢?

还有一种替代的方法,我试过了,但对我没用,不过对其他人有效:

@asynchronous
@gen.engine
def getUrlRes(self,url,params,req_type):
    req1 = tornado.httpclient.HTTPRequest(url="http://www.google.com", method="GET")
    response = yield gen.Task(self.client.fetch,req1)
    self.finish()

2 个回答

2

如果你想调用一个 gen.engine 函数并从中获取返回值,有几个规则需要遵循:

  1. 调用者和被调用者都必须使用 gen.engine 装饰器。需要注意的是,getUrlRes 不需要使用 asynchronous 装饰器,只有 getpost 方法需要这个装饰器。

  2. 被调用的函数需要接受一个 callback 参数。

  3. 实际的调用是通过 yield gen.Task 来完成的。

  4. 被调用的函数通过将值传递给回调函数来返回一个值。

把这些规则结合起来,这里有一个请求处理器,它会获取 google.com 的内容并显示出来:

class Main(tornado.web.RequestHandler):
    client = tornado.httpclient.AsyncHTTPClient()

    @tornado.web.asynchronous
    @gen.engine
    def get(self):
        response = yield gen.Task(self.getUrlRes,
                                  "http://www.google.com", [], "GET")

        print 'got response of length', len(response.body)
        self.finish(response.body)

    @gen.engine
    def getUrlRes(self, url, params, req_type, callback):
        req1 = tornado.httpclient.HTTPRequest(url, method=req_type)
        response = yield gen.Task(self.client.fetch, req1)
        callback(response)

你可以在我的文章中了解更多关于 gen.engine 和子程序的内容,文章链接是 使用 gen.engine 重构 Tornado 代码

顺便提一下,Tornado 3 让这一切变得更简单了,新增了 gen.coroutine 装饰器:

class Main(tornado.web.RequestHandler):
    client = tornado.httpclient.AsyncHTTPClient()

    @gen.coroutine
    def get(self):
        response = yield self.getUrlRes("http://www.google.com", [], "GET")
        print 'got response of length', len(response.body)
        self.finish(response.body)

    @gen.coroutine
    def getUrlRes(self, url, params, req_type):
        req1 = tornado.httpclient.HTTPRequest(url, method=req_type)
        response = yield self.client.fetch(req1)
        raise gen.Return(response)

@asynchronousgen.Task 和显式的回调都不再需要了。函数调用看起来几乎是正常的。协程和普通函数之间主要的区别是 yield 语句,以及使用 raise gen.Return 从子程序返回值。

1

我会使用类似这样的代码

import urllib2

@tornado.web.asynchronous
def getUrlRes(self, url, params, req_type):
    def urlCallBack(response):
        print "Got response {}".format(response)
        self.finish()
    self.client.fetch("http://www.google.com?" + urllib2.urlencode(params), 
                      urlCallBack, method=req_type)

这里的关键是,你需要在回调函数里面调用 self.finish。如果你太早调用 finish,请求就没有时间完成。

如果你想让调用 getUrlRes 的地方能够获取到完成后的 response,你可以传递另一个回调函数,然后在请求完成后调用它:

def caller(self):
    def _callback(response):
        print "Doing something cool"
    getUrlRes("google.com", params, "GET", callback=_callback)

@tornado.web.asynchronous
def getUrlRes(self, url, params, req_type, callback=None):
    def urlCallBack(response):
        print "Got response {}".format(response)
        if callback:
            _callback(response)  # call the passed callback here
        self.finish()
    self.client.fetch("{}?{}".format(url, urllib2.urlencode(params)), 
                      urlCallBack, method=req_type)

撰写回答