【问题标题】:python3.5: with aiohttp is it possible to serve several responses concurently?python3.5:使用 aiohttp 是否可以同时提供多个响应?
【发布时间】:2017-02-02 07:31:43
【问题描述】:

我正在使用最新版本的 aiohttp (1.0.2) 和 python3.5 我有以下服务器代码

import asyncio

from aiohttp.web import Application, Response, StreamResponse, run_app


async def long(request):
    resp = StreamResponse()
    name = request.match_info.get('name', 'Anonymous')
    resp.content_type = 'text/plain'
    for _ in range(1000000):
        answer = ('Hello world\n').encode('utf8')

        await resp.prepare(request)
        resp.write(answer)
    await resp.write_eof()
    return resp


async def init(loop):

    app = Application(loop=loop)
    app.router.add_get('/long', long)
    return app

loop = asyncio.get_event_loop()
app = loop.run_until_complete(init(loop))
run_app(app)

如果我随后在不同的终端运行两个 curl 请求 curl http://localhost:8080/long,则只有第一个会接收数据

我的想法是使用asyncio,您可以在单线程代码中开始提供其他响应,而另一个正在等待 I/O

我在网上找到的大部分关于 concurent+asyncio 的代码只涉及客户端,而不涉及服务器端

我是否遗漏了什么,或者我对 asyncio 工作原理的理解有缺陷?

【问题讨论】:

    标签: asynchronous python-3.5 python-asyncio aiohttp


    【解决方案1】:

    只需在resp.write() 之后按await resp.drain() 即可让aiohttp 有机会在任务之间切换:

    import asyncio
    
    from aiohttp.web import Application, Response, StreamResponse, run_app
    
    
    async def long(request):
        resp = StreamResponse()
        name = request.match_info.get('name', 'Anonymous')
        resp.content_type = 'text/plain'
        await resp.prepare(request)  # prepare should be called once
        for _ in range(1000000):
            answer = ('Hello world\n').encode('utf8')
    
            resp.write(answer)
            await resp.drain()  # switch point
        await resp.write_eof()
        return resp
    
    
    async def init(loop):
    
        app = Application(loop=loop)
        app.router.add_get('/long', long)
        return app
    
    loop = asyncio.get_event_loop()
    app = loop.run_until_complete(init(loop))
    run_app(app)
    

    【讨论】:

    • 谢谢,出于某种原因,我的大脑开始认为await resp.prepare(request) 是做切换点的东西。但是,使用您的代码,在我的两个卷曲中,我仍然只有一个接收数据
    • 可能的原因是:'Hello world\n' 太短,以至于输出缓冲区不堪重负。添加await asyncio.sleep(0) 会显式切换任务。
    • 确实,既然我已经添加了asyncio.sleep(0),我的 2 个卷发正在接收字节(如果我删除对 drain 的调用也可以工作),如果工作量很小,我应该更喜欢这个解决方案吗?字节包一次发送一次?
    • sleep(0) 立即切换任务。 drain() 仅在输出缓冲区过载时切换。因此sleep() 有助于小数据包,但drain 建议用于上传 10M 长文件等实际用例。
    • 我明白了,这是否意味着,在我的情况下,drain() 最终将在 N 足够大的循环的第 N 次迭代之后切换?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-04-23
    • 2021-01-22
    相关资源
    最近更新 更多