allan.simon
allan.simon

Reputation: 4316

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 ?

Upvotes: 1

Views: 405

Answers (1)

Andrew Svetlov
Andrew Svetlov

Reputation: 17366

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)

Upvotes: 2

Related Questions