Вы можете создавать фоновые задачи, которые будут выполнятся после возвращения ответа сервером.
Это может быть полезно для функций, которые должны выполниться после получения запроса, но ожидание их выполнения необязательно для пользователя.
К примеру:
Отправка писем на почту после выполнения каких-либо действий:
Т.к. соединение с почтовым сервером и отправка письма идут достаточно "долго" (несколько секунд), вы можете отправить ответ пользователю, а отправку письма выполнить в фоне.
Обработка данных:
К примеру, если вы получаете файл, который должен пройти через медленный процесс, вы можете отправить ответ "Accepted" (HTTP 202) и отправить работу с файлом в фон.
Сначала импортируйте BackgroundTasks, потом добавьте в функцию параметр с типом BackgroundTasks:
fromfastapiimportBackgroundTasks,FastAPIapp=FastAPI()defwrite_notification(email:str,message=""):withopen("log.txt",mode="w")asemail_file:content=f"notification for {email}: {message}"email_file.write(content)@app.post("/send-notification/{email}")asyncdefsend_notification(email:str,background_tasks:BackgroundTasks):background_tasks.add_task(write_notification,email,message="some notification")return{"message":"Notification sent in the background"}
FastAPI создаст объект класса BackgroundTasks для вас и запишет его в параметр.
Создайте функцию, которую хотите запустить в фоне.
Это совершенно обычная функция, которая может принимать параметры.
Она может быть как асинхронной async def, так и обычной def функцией, FastAPI знает, как правильно ее выполнить.
В нашем примере фоновая задача будет вести запись в файл (симулируя отправку письма).
Так как операция записи не использует async и await, мы определим ее как обычную def:
fromfastapiimportBackgroundTasks,FastAPIapp=FastAPI()defwrite_notification(email:str,message=""):withopen("log.txt",mode="w")asemail_file:content=f"notification for {email}: {message}"email_file.write(content)@app.post("/send-notification/{email}")asyncdefsend_notification(email:str,background_tasks:BackgroundTasks):background_tasks.add_task(write_notification,email,message="some notification")return{"message":"Notification sent in the background"}
Внутри функции вызовите метод .add_task() у объекта background tasks и передайте ему функцию, которую хотите выполнить в фоне:
fromfastapiimportBackgroundTasks,FastAPIapp=FastAPI()defwrite_notification(email:str,message=""):withopen("log.txt",mode="w")asemail_file:content=f"notification for {email}: {message}"email_file.write(content)@app.post("/send-notification/{email}")asyncdefsend_notification(email:str,background_tasks:BackgroundTasks):background_tasks.add_task(write_notification,email,message="some notification")return{"message":"Notification sent in the background"}
.add_task() принимает следующие аргументы:
Функцию, которая будет выполнена в фоне (write_notification). Обратите внимание, что передается объект функции, без скобок.
Любое упорядоченное количество аргументов, которые принимает функция (email).
Любое количество именованных аргументов, которые принимает функция (message="some notification").
Класс BackgroundTasks также работает с системой встраивания зависимостей, вы можете определить BackgroundTasks на разных уровнях: как параметр функции, как завимость, как подзависимость и так далее.
FastAPI знает, что нужно сделать в каждом случае и как переиспользовать тот же объект BackgroundTasks, так чтобы все фоновые задачи собрались и запустились вместе в фоне:
fromfastapiimportBackgroundTasks,Depends,FastAPIapp=FastAPI()defwrite_log(message:str):withopen("log.txt",mode="a")aslog:log.write(message)defget_query(background_tasks:BackgroundTasks,q:str|None=None):ifq:message=f"found query: {q}\n"background_tasks.add_task(write_log,message)returnq@app.post("/send-notification/{email}")asyncdefsend_notification(email:str,background_tasks:BackgroundTasks,q:str=Depends(get_query)):message=f"message to {email}\n"background_tasks.add_task(write_log,message)return{"message":"Message sent"}
fromtypingimportUnionfromfastapiimportBackgroundTasks,Depends,FastAPIapp=FastAPI()defwrite_log(message:str):withopen("log.txt",mode="a")aslog:log.write(message)defget_query(background_tasks:BackgroundTasks,q:Union[str,None]=None):ifq:message=f"found query: {q}\n"background_tasks.add_task(write_log,message)returnq@app.post("/send-notification/{email}")asyncdefsend_notification(email:str,background_tasks:BackgroundTasks,q:str=Depends(get_query)):message=f"message to {email}\n"background_tasks.add_task(write_log,message)return{"message":"Message sent"}
В этом примере сообщения будут записаны в log.txtпосле того, как ответ сервера был отправлен.
Если бы в запрос был передан query-параметр q, он бы первыми записался в log.txt фоновой задачей (потому что вызывается в зависимости get_query).
После другая фоновая задача, которая была сгенерирована в функции, запишет сообщение из параметра email.
Он интегрирован в FastAPI, так что вы можете импортировать его прямо из fastapi и избежать случайного импорта BackgroundTask (без s на конце) из starlette.background.
При использовании BackgroundTasks (а не BackgroundTask), вам достаточно только определить параметр функции с типом BackgroundTasks и FastAPI сделает все за вас, также как при использовании объекта Request.
Вы все равно можете использовать BackgroundTask из starlette в FastAPI, но вам придется самостоятельно создавать объект фоновой задачи и вручную обработать Response внутри него.
Если вам нужно выполнить тяжелые вычисления в фоне, которым необязательно быть запущенными в одном процессе с приложением FastAPI (к примеру, вам не нужны обрабатываемые переменные или вы не хотите делиться памятью процесса и т.д.), вы можете использовать более серьезные инструменты, такие как Celery.
Их тяжелее настраивать, также им нужен брокер сообщений наподобие RabbitMQ или Redis, но зато они позволяют вам запускать фоновые задачи в нескольких процессах и даже на нескольких серверах.
Для примера, посмотрите Project Generators, там есть проект с уже настроенным Celery.
Но если вам нужен доступ к общим переменным и объектам вашего FastAPI приложения или вам нужно выполнять простые фоновые задачи (наподобие отправки письма из примера) вы можете просто использовать BackgroundTasks.