Search code examples
asynchronouspython-3.5python-asyncioaiohttp

python3.5: with aiohttp is it possible to serve several responses concurently?


I'm using the latest version (1.0.2) of aiohttp with python3.5 I have the following server code

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)

If I then run two curl requests curl http://localhost:8080/long in different terminals, only the first one will receive data

My thought was that using asyncio you could, in a monothreaded code, start serving other response, while an other is waiting for I/O

Most of the code I found online about concurent+asyncio only talks about the client side, but not server side

Am I missing something or is my comprehension of how asyncio works is flawed ?


Solution

  • Just push await resp.drain() after resp.write() for giving aiohttp a chance to switch between tasks:

    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)