python Tornado处理程序IO阻止整个服务器网络

时间:2017-12-11 02:04:23

标签: python python-3.x nginx tornado

我用龙卷风实现REST api,并希望它是非阻塞的。

目前,与问题相关的代码如下:

class ReprsHandler(web.RequestHandler):
    async def get(self, name):

        db = await dbf.create_handler()

        if 'id' in list(self.request.query_arguments.keys()):
            db_future = asyncio.ensure_future(db.get_repr(name, self.get_query_argument('id')))
        else:
            db_future = asyncio.ensure_future(db.get_reprs(name))

        result = await db_future
        response = result.toSerializedStream()

        self.set_status(HTTPStatus.OK)
        self.write(response)
        self.set_header('Content-Type', 'text/plain')
        self.finish()


class App(object):
    def __init__(self, loop):
        self.server_app = web.Application(
            handlers=[
                (r"/api/v1/([a-zA-Z0-9_-]+)/reprs", ReprsHandler),
            ]
        )

def main():
    AsyncIOMainLoop().install()
    loop = asyncio.get_event_loop()
    app = App(loop)
    server = tornado.httpserver.HTTPServer(app.server_app, max_body_size=config['max_upload_size'], max_buffer_size=config['max_upload_size'])
    server.bind(config['server_port'])
    server.start()

    loop.run_forever()

简单的代码,但数据非常大,所以发送所有内容大约需要3到4分钟。

我希望处理程序的逻辑和网络IO都是非阻塞的,但它会在发送数据作为响应时阻塞服务器网络。逻辑很好。他们不会阻止其他请求。

详细说明:

  • 此代码在docker,ubuntu 16.04上运行,使用python 3.5实现。
  • 服务器正在使用nginx作为端口代理。

可能有什么问题?我不知道是什么造成了这个问题。

2 个答案:

答案 0 :(得分:1)

由于您提到result.toSerializedStream()腌制数据。所以,是的,你是正确的,阻止是因为网络io。

为避免这种情况,您可以在数据块中发送数据,并在每self.flush()后调用self.write()。调用flush会将响应写入网络。由于您可以在awaitflush,因此在将数据写入网络套接字之前,协程将暂停,服务器不会阻止。这允许其他处理程序异步运行。

代码示例:

async def get(self, name):
    ...
    response = result.toSerializedStream()

    chunk_size = 1024 * 1024 * 10 # 10 MiB

    start_byte = 0
    while True:
        chunk = response[start_byte : start_byte + chunk_size]
        if not chunk:
            break
        self.write(chunk)
        await self.flush() # wait while data is flushed to network

        start_byte += chunk_size # move start_byte forward

重要:

这里要注意的一件重要事情是self.flush()非常快。如果您要将小数据刷新到网络,await延迟非常小,以至于协程会不间断地继续运行,从而阻塞服务器。

在上面的示例代码中,我已将chunk_size设置为10 MiB,但如果您的计算机速度很快,则await延迟将非常非常小,并且在发送整个数据之前,循环可以不间断地运行。

我建议您根据需要增加或减少chunk_size值。

进一步改善建议:

所有数据都在内存中。既然你的处理程序是异步的并且没有阻止,如果ReprsHandler有另一个请求,这将导致更多的数据存储在内存中。如果有越来越多的请求进入,那么你可以告诉我们将会发生什么。

为了避免这种情况,您可以将其转储到文件中,而不是在内存中搜索数据。然后在你的处理程序中只需open该文件并以块的形式读取并发送它。

答案 1 :(得分:0)

行。这个问题太愚蠢了。

我期待这种非阻塞API能够作为并行网络工作,因此整个网络不会互相打断。并不是龙卷风的设计目标。它显然是非阻塞的,但仍然是单线程的。