在python Tornado HTTP服务器

时间:2016-06-19 11:10:12

标签: python tornado

我有各种各样的函数,它们使用阻塞I / O来获取大量数据,并且可以将它写成流/文件类对象(带有分块等);我也有龙卷风HTTP服务器来向客户提供这些数据。至于我无法将所有数据存储在内存中,我应该将其流式传输给客户端,因为我从源代码接收它。所以我写了类似的东西:

import logging

logging.basicConfig(level=logging.DEBUG)

from concurrent.futures import ThreadPoolExecutor
from tornado import gen, httpserver, httpclient, web, ioloop, httputil, escape, locks, iostream
from threading import Event

def get_data(stream):
    with open('/tmp/qq.dat') as file:
        for chunk in iter(lambda: file.read(64*1024), b''):
            stream.write(chunk)

class ProxyStream(object):
    def __init__(self, request):
        self._request = request

    def write(self, data):
        self._request.write(data)
        event = Event()
        self._request.flush(callback=lambda: event.set())
        event.wait()
        return len(data)

class Test(web.RequestHandler):
    def initialize(self, workers):
        self._workers = workers

    @gen.coroutine
    def get(self):
        stream = ProxyStream(self)
        yield self._workers.submit(get_data, stream)
        logging.debug("GET done")
        self.finish()


if __name__ == '__main__':
    workers = ThreadPoolExecutor(4)
    app = web.Application([
        (r"/test", Test, {'workers': workers}),
        ])

    server = httpserver.HTTPServer(app)                                                                                                                                                  server.bind(1488)
    server.start(1)
    ioloop.IOLoop.current().start()

上面的代码,get_data()函数读取一些文件(可能非常大)并将其写入块中以作为参数传递。 Stream由ProxyStream对象模拟,该对象将接收的数据写入RequestHandler对象,等待块刷新到网络。

看来这段代码按预期工作,但我仍然怀疑这种方法是否存在一些陷阱,或者可能有更好的方法来解决这个问题?

1 个答案:

答案 0 :(得分:0)

实际上我遇到了一些问题,导致我找到对我来说更好的解决方案。

RequestHandler write()flush()方法不是线程安全的,应该从正在运行ioloop的线程中调用(请参阅thisvalid jwt token)。所以正确的方法是包裹write()& flush()进入IOLoop.add_callback,以便在下一次ioloop次迭代时调用它们。 结果代码是这样的:

class ProxyStream(object):
def __init__(self, handler, headers=[]):
    self._handler = handler

def sync_write(self, data, event):
    self._handler.write(data)
    self._handler.flush(callback=lambda: event.set())

def write(self, data):
    if not self._handler.dead:
        event = Event()
        IOLoop.current().add_callback(self.sync_write, data, event)
        event.wait()
        return len(data)
    else:
        raise IOError("Client has closed connection")

RequestHandler将自己传递给某些同步代码,应将self.dead = True设置为on_connection_close(),以便在客户端断开连接后停止流式传输。