Планирование задач — неотъемлемая часть работы любых веб-приложений, в особенности таких, в которых требуется совершать периодические или отложенные действия. В Python предусмотрено множество способов планирования задач, и у каждого есть свои сильные и слабые стороны. В этой статье будут рассмотрены некоторые наиболее популярные способы планирования задач в приложении, написанном на основе FastAPI.
Модуль
Вот как при помощи
В вышеприведённом коде мы создаём объект
Конечно,
Пакет
Пакет
На следующем примере показано, как при помощи
В вышеприведённом коде мы определяем функцию
Притом, что
Использование повторяющихся задач из
Пакет
Ниже показано, как при помощи
В вышеприведённом коде при помощи декоратора
Пакет
Использование пакета
Пакет
Вот как при помощи
В вышеприведённом коде мы при помощи функции
Работа с
Celery — это популярный инструмент для работы с очередью распределённых задач, и его удобно применять при работе с приложением на FastAPI. В Celery поддерживаются различные продвинутые возможности, в частности, расстановка приоритетов задач, стратегии повторных попыток и хранилище для результатов задач. Подробнее о периодических задачах рассказано в документации по Celery.
Вот как при помощи Celery можно запланировать задачу в приложении на FastAPI:
В вышеприведённом коде при помощи метода
Celery — это мощная и насыщенная возможностями библиотека для планирования задач, справляющаяся с очень сложными сценариями, но достаточно сложная в настройке и конфигурировании.
Работа с
Dramatiq — это высокопроизводительная библиотека для обработки распределённых задач, применимая с приложениями на FastAPI. В ней также поддерживаются такие возможности как расстановка приоритетов задач, стратегии повторных попыток и хранилище для результатов.
Вот как при помощи Dramatiq можно запланировать задачу в приложении на FastAPI:
В вышеприведённом коде мы применяем метод
Библиотека
Вот как при помощи
В вышеприведённом коде мы задаём при помощи библиотеки
Обратите внимание: асинхронный планировщик создаётся при помощи класса
В целом
При работе с приложениями, написанными на Python с применением FastAPI есть множество инструментов для планирования задач, каждый со своими достоинствами и недостатками. Подходящая библиотека для планирования выбирается в зависимости от сложности вашего приложения и от того, какие именно возможности вам требуются.
Если вам нужна простая и удобная в использовании библиотека для планирования, то вам хорошо подойдут варианты
Независимо от того, на какой библиотеке вы остановитесь, можно значительно усовершенствовать функциональность и эффективность вашего приложения на FastAPI, если настроить в нём планирование задач. Надеюсь, что приведённые в этой статье примеры кода станут для вас хорошей отправной точкой, чтобы реализовать планирование задач в ваших проектах.
sched
– планировщик событий из Python
Модуль
sched
входит в состав стандартной библиотеки Python и обеспечивает простой механизм для планирования событий в программе. Этот модуль может работать в приложении на FastAPI, но пользоваться им не рекомендуется, так как он слишком прост, и функциональность его ограничена.Вот как при помощи
sched
можно запланировать задачу в приложении на FastAPI:import sched
import time
scheduler = sched.scheduler(time.time, time.sleep)
def run_me_every_minute():
print("Running every minute...")
def schedule_next_event():
scheduler.enter(60, 1, run_me_every_minute)
scheduler.run()
scheduler.enter(60, 1, run_me_every_minute)
while True:
schedule_next_event()
В вышеприведённом коде мы создаём объект
scheduler
при помощи модуля sched
и определяем функцию run_me_every_minute
так, чтобы она работала как запланированная задача. Затем применяем метод enter
, чтобы он назначал задачу, и она начинала выполняться через 60 секунд, а при помощи метода run
запускаем планировщик.Конечно,
sched
прост и лёгок в использовании, но в нём не хватает некоторых продвинутых возможностей, имеющихся в других библиотеках для планирования задач.Пакет schedule
для python
Пакет
schedule
(см. Github, см. документация) позиционируется как «планировщик задач Python, сделанный для людей». Он предоставляет мощные и гибкие способы планировать задачи в Python. Им удобно пользоваться и у него простой API.На следующем примере показано, как при помощи
schedule
запланировать задачу в приложении на FastAPI:import schedule
import time
def run_me_every_minute():
print("Running every minute...")
schedule.every(1).minutes.do(run_me_every_minute)
while True:
schedule.run_pending()
time.sleep(1)
В вышеприведённом коде мы определяем функцию
run_me_every_minute
так, чтобы она выполнялась как запланированная задача, а при помощи метода every
задаём выполнение этой задачи раз в минуту. Далее при помощи метода run_pending
мы проверяем, какие задачи назначены, а при помощи метода sleep
устанавливаем задержку цикла на 1 секунду.Притом, что
schedule
прост в использовании, он ещё и предоставляет многие продвинутые возможности — в частности, обеспечивает обработку ошибок и подбор интервалов. Тем не менее, он может работать только в однопоточном режиме и для высокопроизводительных приложений не подойдёт.Использование повторяющихся задач из fastapi-utils
Пакет
fastapi-utils
(см. Github, см. документация) позволяет просто и с удобством назначать повторяющиеся задачи в приложении, написанном на FastAPI. Для обработки асинхронных задач в нём используется библиотека asyncio.Ниже показано, как при помощи
fastapi-utils
назначить задачу в приложении на FastAPI:from fastapi import FastAPI
from fastapi_utils.tasks import repeat_every
app = FastAPI()
@repeat_every(seconds=60)
async def run_me_every_minute():
print("Running every minute...")
if __name__ == "__main__":
import uvicorn
uvicorn.run(app)
В вышеприведённом коде при помощи декоратора
repeat_every
мы назначаем выполнение задачи с периодичностью в 60 секунд. Далее мы определяем функцию run_me_every_minute
так, чтобы она выполнялась как назначенная задача.Пакет
fastapi-utils
прост в использовании и хорошо интегрируется с FastAPI, но, возможно, не подойдёт для сравнительно сложных задач, связанных с планированием. Использование пакета arq
Пакет
arq
(см. Github, см. документация) — ещё один мощный и гибкий инструмент для назначения задач в приложениях на FastAPI. Он выстроен на основе библиотеки asyncio
и поддерживает продвинутые возможности: в частности, позволяет присваивать задачам приоритет и формулировать стратегии повторных попыток.Вот как при помощи
arq
назначить задачу в приложении на FastAPI:from fastapi import FastAPI
from arq import create_pool
from arq.jobs import Job
app = FastAPI()
async def run_me_every_minute():
print("Running every minute...")
@app.on_event("startup")
async def startup():
redis_settings = {"host": "localhost", "port": 6379}
pool = await create_pool(redis_settings)
job = Job(run_me_every_minute)
await pool.enqueue_job(job)
if __name__ == "__main__":
import uvicorn
uvicorn.run(app)
В вышеприведённом коде мы при помощи функции
create_pool
создаём пул соединений с Redis, который arq
затем использует для хранения и назначения задач. Мы определяем функцию run_me_every_minute
так, чтобы она выполнялась как назначенная задача, а также инкапсулируем её в объект Job
, который специально для этого создаём. Наконец, применяем метод enqueue_job
, чтобы назначить эту задачу на выполнение.arq
— мощная и гибкая библиотека для планирования, справляющаяся со сложными задачами. Но с её настройкой и конфигурацией приходится поработать серьёзнее, чем в случае с некоторыми другими библиотеками.Работа с celery
Celery — это популярный инструмент для работы с очередью распределённых задач, и его удобно применять при работе с приложением на FastAPI. В Celery поддерживаются различные продвинутые возможности, в частности, расстановка приоритетов задач, стратегии повторных попыток и хранилище для результатов задач. Подробнее о периодических задачах рассказано в документации по Celery.
Вот как при помощи Celery можно запланировать задачу в приложении на FastAPI:
from fastapi import FastAPI
from celery import Celery
app = FastAPI()
celery = Celery('tasks', broker='pyamqp://guest@localhost//')
@celery.task
def run_me_every_minute():
print("Running every minute...")
@app.on_event("startup")
async def startup():
celery.conf.beat_schedule = {
"run-every-minute": {
"task": "main.run_me_every_minute",
"schedule": crontab(minute="*")
}
}
celery.conf.timezone = "UTC"
celery.conf.task_routes = {"main.run_me_every_minute": {"queue": "default"}}
celery.autodiscover_tasks(["main"])
# Other Celery config
if __name__ == "__main__":
import uvicorn
uvicorn.run(app)
В вышеприведённом коде при помощи метода
celery.schedules.crontab
мы назначаем для Celery дополнительные конфигурационные настройки, так, чтобы задача run_me_every_minute
выполнялась каждую минуту. Также мы определим для Celery конфигурационные настройки, в которых указывается часовой пояс и настройки маршрутизации задач.Celery — это мощная и насыщенная возможностями библиотека для планирования задач, справляющаяся с очень сложными сценариями, но достаточно сложная в настройке и конфигурировании.
Работа с Dramatiq
Dramatiq — это высокопроизводительная библиотека для обработки распределённых задач, применимая с приложениями на FastAPI. В ней также поддерживаются такие возможности как расстановка приоритетов задач, стратегии повторных попыток и хранилище для результатов.
Вот как при помощи Dramatiq можно запланировать задачу в приложении на FastAPI:
from fastapi import FastAPI
from dramatiq import pipeline, actor, run_pipeline, cron
app = FastAPI()
@actor(cron("*/1 * * * *"))
def run_me_every_minute():
print("Running every minute...")
@app.on_event("startup")
async def startup():
job = pipeline(run_me_every_minute.message())
run_pipeline(job)
if __name__ == "__main__":
import uvicorn
uvicorn.run(app)
В вышеприведённом коде мы применяем метод
dramatiq.cron
, чтобы назначить ежеминутное выполнение задачи run_me_every_minute
. Задача cron назначает для выполнения при помощи выражения */1 * * * *
, означающего, что мы мы создаём конвейер, а затем задействуем методы pipeline
и run_pipeline
для назначения и выполнения задачи.APScheduler
Библиотека
APScheduler
также очень мощная и пользуется популярностью при планировании задач на Python. В ней предусмотрено много продвинутых возможностей, в частности, планирование в стиле cron, интервальное планирование и многое другое.Вот как при помощи
APScheduler
можно назначить, чтобы выбранная задача ежеминутно выполнялась в приложении на FastAPI:from fastapi import FastAPI
from apscheduler.schedulers.asyncio import AsyncIOScheduler
app = FastAPI()
scheduler = AsyncIOScheduler()
async def run_me_every_minute():
print("Running every minute...")
@app.on_event("startup")
async def startup():
scheduler.add_job(run_me_every_minute, "interval", minutes=1)
scheduler.start()
if __name__ == "__main__":
import uvicorn
uvicorn.run(app)
В вышеприведённом коде мы задаём при помощи библиотеки
APScheduler
, чтобы задача run_me_every_minute
выполнялась ежеминутно. Мы определяем объект планировщика, а далее при помощи метода add_job
добавляем задачу в планировщик. Далее для запуска планировщика вызывается метод start
.Обратите внимание: асинхронный планировщик создаётся при помощи класса
AsyncIOScheduler
. Он необходим при использовании APScheduler
с FastAPI, поскольку FastAPI – это асинхронный фреймворк.В целом
APScheduler
— это мощная библиотека для планирования, в которой предоставляется множество продвинутых возможностей. Она совместима с FastAPI и может использоваться при планировании задач, когда они должны выполняться с разными интервалами.Заключение
При работе с приложениями, написанными на Python с применением FastAPI есть множество инструментов для планирования задач, каждый со своими достоинствами и недостатками. Подходящая библиотека для планирования выбирается в зависимости от сложности вашего приложения и от того, какие именно возможности вам требуются.
Если вам нужна простая и удобная в использовании библиотека для планирования, то вам хорошо подойдут варианты
schedule
или fastapi-utils
. Если вам требуются более продвинутые возможности и более значительная гибкость при работе, попробуйте arq
, Celery или Dramatiq.Независимо от того, на какой библиотеке вы остановитесь, можно значительно усовершенствовать функциональность и эффективность вашего приложения на FastAPI, если настроить в нём планирование задач. Надеюсь, что приведённые в этой статье примеры кода станут для вас хорошей отправной точкой, чтобы реализовать планирование задач в ваших проектах.
baldr
Для celery пример совершенно неверный. Необходимо указать что, помимо вашего FastAPI-приложения еще должно быть запущено как минимум два процесса - celery worker и celery beat (непосредственно шедулер). Конфиг для шедулера в том виде как он написан в примере - не будет применен, нужно его делать в отдельном файле, который celery сможет импортировать.
В принципе, FastAPI здесь вообще не нужен, как и во всех примерах - вы же говорите о регулярных задачах, а не о вызове background-задач из веб-фреймворке.
Andrey_Solomatin
Есть небольшой нюанс, в примерах сервер запускается как один процесс. То есть у нас будет ровно один инстанс планировщика.
Если взять Django, то его обычно запускают во много процессов и соответственно будет много запусков запланированных задач. Этот момент в статье как-то не раскрыт.
Как вы справидливо заметили celery решает эту проблему через отдельный процесс для пларировщика. Такой подход, действительно, не зависит от фреймворка.
baldr
Наверное, вы имели в виду инстанс воркера? Планировщик запускается отдельно.
А вообще говоря, если поднимать тему глубже и говорить об использовании celery из-под FastAPI, то все виденные мною примеры (втч и на хабре) делают фундаментальную ошибку - они забывают, что celery - это не async приложение. А вызывают его таски во всех примерах из async функции. Я пытаюсь обращать внимание на это в комментариях в каждой статье, а также приводил пример как это, в принципе, можно сделать асинхронно.
Однако - всё-таки подумайте трижды, прежде чем брать celery в проект.