【问题标题】:Tornado streaming HTTP response as AsyncHTTPClient receives chunks当 AsyncHTTPClient 接收块时,Tornado 流式传输 HTTP 响应
【发布时间】:2013-11-16 12:53:41
【问题描述】:

我正在尝试编写一个 Tornado 请求处理程序,它发出异步 HTTP 请求,并在客户端从异步请求接收数据时将数据返回给客户端。不幸的是,在所有异步 HTTPRequest 完成之前,我无法让 Tornado 向客户端返回任何数据。

我的请求处理程序的演示如下。

类 StreamingHandler(web.RequestHandler): all_requested = False 请求 = [] @web.asynchronous 定义获取(自我): http_client = httpclient.AsyncHTTPClient() self.write('一些开口') big_request = httpclient.HTTPRequest(url='[some_big_request]', streaming_callback=self.on_chunk) small_request = httpclient.HTTPRequest(url='[some_small_request]', streaming_callback=self.on_chunk) self.requests.append(http_client.fetch(big_request, callback=self.on_response_complete)) self.requests.append(http_client.fetch(small_request, callback=self.on_response_complete)) self.all_requested = True def on_chunk(自我,块): self.write('一些块') self.flush() def on_response_complete(自我,响应): if self.all_requested and all(request.done() for request in self.requests): self.write('一些关闭') self.finish()

我希望对这个处理程序的 GET 请求最初返回文本“some opening”,然后很快为小请求返回“some chunk”,然后为更大的请求返回“some chunk”(可能多次)请求,在最终返回“关闭”并关闭连接之前。相反,在建立连接后,客户端会等待几秒钟以等待所有请求完成,然后在关闭之前立即接收所有 HTTPResponse。

我将如何从 Tornado 获得我想要的行为?

提前致谢!

【问题讨论】:

    标签: python asynchronous tornado


    【解决方案1】:

    gen.coroutine 装饰您的方法并生成期货列表。这是一个简单的例子:

    from tornado import gen, web, httpclient
    
    class StreamingHandler(web.RequestHandler):
        @web.asynchronous
        @gen.coroutine
        def get(self):
            client = httpclient.AsyncHTTPClient()
    
            self.write('some opening')
            self.flush()
    
            requests = [
                httpclient.HTTPRequest(
                    url='http://httpbin.org/delay/' + str(delay),
                    streaming_callback=self.on_chunk
                ) for delay in [5, 4, 3, 2, 1]
            ]
    
            # `map()` doesn't return a list in Python 3
            yield list(map(client.fetch, requests))
    
            self.write('some closing')
            self.finish()
    
        def on_chunk(self, chunk):
            self.write('some chunk')
            self.flush()
    

    请注意,即使请求是“向后”产生的,大约一秒钟后仍会收到第一个块。如果您将它们同步发送出去,则需要 15 秒。当您异步请求它们时,只需 5 个。

    【讨论】:

    • 嗨,谢谢。不过有几个问题:您能解释一下为什么我的代码不起作用而您的代码起作用吗?这似乎归结为 tornado.gen.coroutine 对“yield”的一些神秘使用。你怎么能保证一旦你产生了一个期货列表,请求是完整的并且写“一些关闭”是安全的?
    • @majackson:这就是 Tornado 实现协程的方式。通过使用yield,您的函数变成了一个生成器,因此您可以在您执行yield 任务的位置有效地启动和停止它。当您yield 一个任务(或任务列表,Tornado 一次执行所有任务)时,您告诉 Tornado 的 IOLoop 它可以“停止”您的函数的执行并执行其他操作。当它“回到”你的功能时,它会从它停止的地方开始。您的函数代码仍会从上到下执行,但 Tornado 也会跳转到其他函数。这有点像 Node.js 的做法。
    猜你喜欢
    • 1970-01-01
    • 2018-02-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-04-02
    • 1970-01-01
    • 1970-01-01
    • 2023-04-01
    相关资源
    最近更新 更多