У меня есть простая конечная точка fastAPI, развернутая в Google Cloud Run. Я сам написал класс Workflow
. Когда экземпляр Workflow
выполняется, происходят некоторые шаги, например, файлы обрабатываются, и результат помещается в базу данных vectorstore.
Обычно это занимает несколько секунд для каждого файла, такого как этот:
from .workflow import Workflow
...
@app.post('/execute_workflow_directly')
async def execute_workflow_directly(request: Request)
... # get files from request object
workflow = Workflow.get_simple_workflow(files=files)
workflow.execute()
return JSONResponse(status_code=200, content = {'message': 'Successfully processed files'})
Теперь, если задействовано много файлов, это может занять некоторое время, и я не хочу, чтобы вызывающий абонент конечной точки ждал, поэтому я хочу запустить выполнение рабочего процесса в фоновом режиме следующим образом:
from .workflow import Workflow
from fastapi import BackgroundTasks
...
def run_workflow_in_background(workflow: Workflow):
workflow.execute()
@app.post('/execute_workflow_in_background')
async def execute_workflow_in_background(request: Request, background_tasks: BackgroundTasks):
... # get files from request object
workflow = Workflow.get_simple_workflow(files=files)
background_tasks.add_task(run_workflow_in_background, workflow)
return JSONResponse(status_code=202, content = {'message': 'File processing started'})
Тестируя это только с одним файлом, я уже столкнулся с проблемой: локально он работает нормально, но когда я развертываю его в своей службе Google Cloud Run, время выполнения зашкаливает: в одном примере фоновое выполнение заняло почти ~ 500 секунд, пока я не увидел результат в базе данных, по сравнению с ~ 5 секундами при непосредственном выполнении рабочего процесса.
Я уже пытался увеличить количество ядер ЦП до 4, а впоследствии и количество рабочих стрелков до 4. Не уверен, что это имеет смысл, но время выполнения не уменьшилось.
Могу ли я решить эту проблему, выделив каким-то образом больше ресурсов для запуска Google Cloud, или мой подход ошибочен, и я делаю что-то не так, или уже должен переключиться на что-то более сложное, например, Celery?
Изменить (не имеет отношения к моей проблеме, см. принятый ответ):
Я прочитал принятый ответ на этот вопрос, и он помог прояснить некоторые вещи, но на самом деле не отвечает на мой вопрос, почему существует такая большая разница во времени выполнения между запуском напрямую и в качестве фоновой задачи. Обе версии вызывают интенсивный процессор workflow.execute()
асинхронно, если я не ошибаюсь.
Я действительно не могу изменить определение конечной точки на def
, потому что я ожидаю другого кода внутри.
Я попытался изменить фоновую функцию на
async def run_workflow_in_background(workflow: Workflow):
await run_in_threadpool(workflow.execute)
и
async def run_workflow_in_background(workflow: Workflow):
loop = asyncio.get_running_loop()
with concurrent.futures.ThreadPoolExecutor() as pool:
res = await loop.run_in_executor(pool, workflow.execute)
и
async def run_workflow_in_background(workflow: Workflow):
res = await asyncio.to_thread(workflow.execute)
и
async def run_workflow_in_background(workflow: Workflow):
loop = asyncio.get_running_loop()
with concurrent.futures.ProcessPoolExecutor() as pool:
res = await loop.run_in_executor(pool, workflow.execute)
как было предложено, и это не помогло.
Я попытался увеличить количество рабочих, как было предложено, и это не помогло.
Думаю, я рассмотрю возможность перехода на Celery, но все же хочу понять, почему он так медленно работает с фоновыми задачами fastAPI.
@Chris: Не совсем так, посмотри мои правки. Может быть, все еще мое отсутствие полного понимания.
Как выглядят ваши настройки Cloud Run? Как описано в этого ответа, если вы не установите для минимального экземпляра значение не менее 1, а для ЦП всегда установлено значение true, Cloud Run будет ограничивать доступ вашего ЦП, как только завершится HTTP-запрос, что звучит точно так же, как вы повторное описание.
В Cloud Function, как и в Cloud Run, ЦП выделяется (и оплачивается) только при обработке запроса.
Запрос считается обработанным между получением запроса и отправкой ответа.
В остальное время ЦП дросселируется (ниже 5%).
Как говорится, оглянитесь на свои функции.
Чтобы решить эту проблему, вы можете использовать Cloud Run с опцией ЦП всегда выделяется (или без дросселирования процессора с помощью командной строки GCLOUD). Нет варианта с облачными функциями
Спасибо, это была именно проблема. Хорошее объяснение, имеет много смысла. Конечно, это не идеально с точки зрения затрат, когда эти фоновые задачи выполняются всего несколько раз в день. Есть ли способ убедиться, что у фоновых задач достаточно ресурсов, не всегда выделяя их?
Отвечает ли это на ваш вопрос? FastAPI запускает API-вызовы последовательно, а не параллельно