python tornado 异步客户端提取调用和回调有时会失败

python tornado async client fetch call with call back fails some times

当我使用 Tornado 异步调用请求页面时,有时它会成功获取页面内容,但有时它无法从我的本地服务器获取内容。

没有意义如何处理回调。

在我的本地 LAMP 服务器上,此代码每页保留 1 秒。 // b.php

    <?php
    header( 'Content-type: text/html; charset=utf-8' );
    ob_implicit_flush();
    foreach (range(1,3) as $x){
      echo $x;
      usleep(333333);
    }
    ?>

// c.php

    <?php
    header( 'Content-type: text/html; charset=utf-8' );
    ob_implicit_flush();
    foreach (range(4,6) as $x){
      echo $x;
      usleep(333333);
    }
    ?>

// d.php

    <?php
    header( 'Content-type: text/html; charset=utf-8' );
    ob_implicit_flush();
    foreach (range(7,9) as $x){
      echo $x;
      usleep(333333);
    }
    ?>

在 python 网络客户端/服务器上

我关心的代码。

我的回答是为什么它不起作用,最后一个调用在第一个调用之前返回。因此它将完成回调并且无法执行其他回调以将其写入屏幕但它应该触发所有请求并收集响应然后应该完成。任何想法? 如果可能的话,我该如何修改这种代码风格而不是使用 yield

    import tornado.httpserver
    import tornado.ioloop
    import tornado.options
    import tornado.web
    import tornado.httpclient
    from tornado.options import define, options
    import tornado.gen
    
    define("port", default=8000, help="run on the given port", type=int)
    class IndexHandler(tornado.web.RequestHandler):
        @tornado.web.asynchronous
        @tornado.gen.engine
        def get(self):
            client = tornado.httpclient.AsyncHTTPClient()
            client.fetch("http://www.droid-life.com/?" + \
                         urllib.urlencode({"s": query}), callback=self.on_response)
            client.fetch("http://localhost/b.php", callback=self.on_response)
            client.fetch("http://localhost/c.php", callback=self.on_response)
            client.fetch("http://localhost/d.php", callback=self.on_response3)
        @tornado.web.asynchronous
        def on_response(self, response):
            body = (response.body)
            self.write(body)
        @tornado.web.asynchronous
        def on_response2(self, response):
            body = (response.body)
            self.write(body)
            client = tornado.httpclient.AsyncHTTPClient()
            client.fetch("http://localhost/d.php", callback=self.on_response3)
        def on_response3(self, response):
            body = (response.body)
            self.write(body)
            self.finish()
    if __name__ == "__main__":
        tornado.options.parse_command_line()
        app = tornado.web.Application(handlers=[(r"/", IndexHandler)])
        http_server = tornado.httpserver.HTTPServer(app)
        http_server.listen(options.port)
        tornado.ioloop.IOLoop.instance().start()

此代码确实有效,并且 returns 在合理的时间内产生结果,大约 1.1~ 1.3 秒

    define("port", default=8000, help="run on the given port", type=int)
    class IndexHandler(tornado.web.RequestHandler):
        @tornado.web.asynchronous
        @tornado.gen.engine
        def get(self):
    
            client = tornado.httpclient.AsyncHTTPClient()
            r1,r2,r3 = yield [client.fetch("http://localhost/b.php"), \
                           client.fetch("http://localhost/c.php"), \
                           client.fetch("http://localhost/d.php") \
                            ]
            self.write(r1.body)
            self.write(r2.body)
            self.write(r3.body)
            self.finish()
    if __name__ == "__main__":
        tornado.options.parse_command_line()
        app = tornado.web.Application(handlers=[(r"/", IndexHandler)])
        http_server = tornado.httpserver.HTTPServer(app)
        http_server.listen(options.port)
        tornado.ioloop.IOLoop.instance().start()

你的问题不清楚事情究竟是如何失败的(以及为什么第二个版本既短又有效,不是一个可接受的解决方案),但我看到的一件事是你的代码调用的第一个版本on_response3(调用 finish)两次:一次来自 get(),一次来自 on_response2。一旦 on_response3 被调用,您的处理程序将通过首先完成的代码路径停止。

如果您想以回调方式并行执行三个提取,则必须保留一个未完成提取数的计数器,这样您才能在所有三个提取都完成后才调用完成。你的第二个例子的基于回调的等价物是这样的:

class IndexHandler(RequestHandler):
    @asynchronous
    def get(self):
        client = tornado.httpclient.AsyncHTTPClient()
        self.remaining = 3
        self.responses = {}
        client.fetch("http://localhost/b.php", functools.partial(self.on_response, 'b'))
        client.fetch("http://localhost/c.php", functools.partial(self.on_response, 'c'))
        client.fetch("http://localhost/d.php", functools.partial(self.on_response, 'd'))

    def on_response(self, key, response):
        self.responses[key] = response
        self.remaining -= 1
        if self.remaining == 0:
            self.write(self.responses['b'].body)
            self.write(self.responses['c'].body)
            self.write(self.responses['d'].body)
            self.finish()