Асинхронный REST API Python с ответами, которые требуют вычислений с интенсивным использованием ЦП. Как эффективно обращаться?

Я написал базовый REST API, используя aiohttp, упрощенная версия которого ниже, чтобы проиллюстрировать проблему, которую я хочу решить.

API имеет две конечные точки, каждая из которых вызывает функцию, выполняющую некоторые вычисления. Разница между ними в том, что для одной из конечных точек вычисления занимают 10 секунд, а для другой всего 1 секунду.

Мой код ниже (фактические вычисления были заменены вызовами time.sleep()).

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)

Что я хотел бы сделать:

Если я отправлю запрос на более медленную конечную точку, а затем сразу же отправлю запрос на более быструю конечную точку, я хотел бы сначала получить ответ от более быстрой конечной точки, в то время как более медленные вычисления все еще продолжаются.

Что происходит на самом деле:

Вычисления, выполняемые более медленной конечной точкой, блокируются. Я получаю ответ от медленной конечной точки через ~10 секунд и от быстрой конечной точки через ~11 секунд.

Последние несколько часов я ходил по кругу, читая asyncio и multiprocessing, но не смог найти ничего, что могло бы решить мою проблему. Вероятно, мне нужно потратить немного больше времени на изучение этой области, чтобы лучше понять ее, но я надеюсь, что смогу получить толчок в правильном направлении к желаемому результату.


person thesilkworm    schedule 14.09.2018    source источник
comment
Asyncio очень плохо работает с задачами, связанными с ЦП, многопроцессорность — это способ решить эту проблему, aioprocessing делает очень хорошо работа смешивая оба   -  person yorodm    schedule 14.09.2018
comment
@yorodm спасибо, почитаю.   -  person thesilkworm    schedule 14.09.2018


Ответы (1)


В asyncio следует избегать любых блокирующих вызовов ввода-вывода.

По сути, time.sleep(10) блокирует весь сервер aiohttp на 10 секунд.

Чтобы решить эту проблему, используйте loop.run_in_executor () вызов:

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}
person Andrew Svetlov    schedule 14.09.2018
comment
Спасибо - это привело меня на правильный путь, но я должен добавить для всех, кто читает, что мне пришлось вставить None в качестве первого аргумента: loop.run_in_executor(None, time.sleep, 10). - person thesilkworm; 15.09.2018
comment
Спасибо, обновил ответ, добавив None - person Andrew Svetlov; 16.09.2018