Search code examples
pythonmultiprocessingpython-3.6aiohttp

Python asynchronous REST API with responses which rely on CPU intensive calculations. How to handle efficiently?


I have written a basic REST API using aiohttp, a simplified version of which is included below to illustrate the problem I am looking to solve.

The API has two endpoints - each of which calls a function that performs some calculations. The difference between the two is that for one of the endpoints, the calculations take 10 seconds, and for the other they take only 1 second.

My code is below (the actual calculations have been replaced with time.sleep() calls).

import time
from aiohttp import web


def simple_calcs():
    time.sleep(1)  # Pretend this is the simple calculations
    return {'test': 123}

def complex_calcs():
    time.sleep(10)  # Pretend this is the complex calculations
    return {'test': 456}


routes = web.RouteTableDef()

@routes.get('/simple_calcs')
async def simple_calcs_handler(request):
    results = simple_calcs()
    return web.json_response(results)

@routes.get('/complex_calcs')
async def complex_calcs_handler(request):
    results = complex_calcs()
    return web.json_response(results)


app = web.Application()
app.add_routes(routes)
web.run_app(app)

What I would like to happen:

If I send a request to the slower endpoint, then immediately afterwards send a request to the faster endpoint, I would like to receive a response from the faster endpoint first while the slower calculations are still ongoing.

What actually happens:

The calculations being carried out by the slower endpoint are blocking. I receive the response from the slow endpoint after ~10 seconds and from the fast endpoint after ~11 seconds.

I've spent the last few hours going round in circles, reading up on asyncio and multiprocessing, but unable to find anything that could solve my problem. Probably I need to spend a bit longer studying this area to gain a better understanding, but hoping I can get a push in the right direction towards the desired outcome.


Solution

  • Any blocking IO calls should be avoided in asyncio.

    Essentially time.sleep(10) blocks the whole aiohttp server for 10 seconds.

    To solve it please use loop.run_in_executor() call:

    async def complex_calcs():
        loop = asyncio.get_event_loop()
        loop.run_in_executor(None, time.sleep, 10)  # Pretend this is the complex calculations
        return {'test': 456}