Celery: контрольный список из 17 лучших практик

О лучших практиках Celery можно найти достаточно материала, в том числе в официальной документации. Но удобно иметь все основные рекомендации в одном месте в формате контрольного списка.

  1. В качестве брокера используйте RabbitMQ или Redis (никогда не используйте реляционную базу данных в качестве брокера).

  2. Не используйте сложные объекты (например, объекты Django-моделей) в задаче в качестве параметров.

  3. Внутри одной задачи не ожидайте выполнения других задач.

  4. Отдавайте предпочтение идемпотентным задачам (таким, которые при повторном выполнении будут давать тот же результат, что и при первом).

  5. Отдавайте предпочтение атомарным задачам (задачи, которые нельзя разбить на подзадачи).

  6. Не храните результаты выполнения без необходимости.

  7. Логируйте ошибки. Полная картина происходящего внутри кода сэкономит драгоценное время при анализе проблем. Обратите внимание на Sentry — удобный инструмент мониторинга ошибок.

  8. Используйте механизмы для повторного запуска задач. Но сначала убедитесь, что задачи являются идемпотентными и атомарными.

  9. Установите retry_limit, чтобы сломанные задачи не перезапускались бесконечно.

  10. Используйте экспоненциальное откладывание, если ошибки не будут исправлены в ближайшее время. Добавьте случайный фактор.

    def exponential_backoff(task_self):
        minutes = task_self.default_retry_delay / 60
        rand = random.uniform(minutes, minutes * 1.3)
    return int(rand ** task_self.request.retries) * 60
    
    # в задаче
    raise self.retry(exc=e, countdown=exponential_backoff(self))
    

    Чем данный способ вычисления повтора лучше повтора через фиксированный промежуток времени:

    • интервал увеличивается с каждой попыткой, если сервер не может справиться с потоком запросов, поток запросов от клиентов будет уменьшаться со временем;
    • интервалы рандомизированы, т. е. не будет «шквала» запросов в такты, пропорциональные фиксированному интервалу.
  11. Используйте autoretry_for, чтобы уменьшить шаблонность кода в задачах с механизмом повторения.

  12. Используйте retry_backoff, чтобы уменьшить шаблонность кода при выполнении экспоненциального откладывания.

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

  14. Установите жесткие и мягкие временные лимиты. Используйте аккуратное восстановление, если что-то займет больше времени, чем ожидалось.

    from celery.exceptions import SoftTimeLimitExceeded
    
    @app.task(task_time_limit=60, task_soft_time_limit=45)
    def my_task():
    try:
        something_possibly_long()
    except SoftTimeLimitExceeded:
        recover()
    
  15. Разделяйте задачи по очередям, чтобы лучше контролировать пропускную способность и добиться большей масштабируемости. Если все задачи складывать в одну очередь, то однажды она может забиться, поставив под угрозу выполнение критически важного кода.

  16. Расширьте базовый класс задачи, чтобы определить поведение по умолчанию (см. классы задач, определяемые пользователем).

  17. Используйте возможности celery.canvas для управления потоками и решения задач, связанных конкурентностью.

Рекомендую сервис, на котором удобно проверять себя по наиболее важным пунктам из списка выше: celerytaskschecklist.com.