← Назад к вопросам

Celery выполняются в отдельном потоке

2.0 Middle🔥 131 комментариев
#Асинхронность и многопоточность#Брокеры сообщений

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

# Celery: Выполнение в отдельном потоке

Это не совсем точное утверждение. Celery — это не просто выполнение кода в отдельном потоке. Это распределённая очередь задач (distributed task queue), которая выполняет задачи в отдельных процессах на отдельных машинах.

Чем Celery НЕ является

❌ Не threading (многопоточность)

import threading

# Это просто поток в одном процессе
def background_job():
    print('Работаю в потоке')

thread = threading.Thread(target=background_job)
thread.start()

Проблемы:

  • Все потоки в одном процессе
  • Если одна задача упадёт — может упасть всё
  • GIL (Global Interpreter Lock) снижает параллелизм
  • Нельзя масштабировать на несколько машин
  • При перезагрузке приложения потеряются невыполненные задачи

Что такое Celery

Архитектура

┌─────────────────────┐
│  Django приложение  │  Отправляет
│  (Web Server)       │  задачи
└──────────┬──────────┘
           │
           │ send_task()
           │
┌──────────▼──────────┐
│  Message Broker     │  RabbitMQ / Redis
│  (очередь задач)    │  Хранит задачи
└──────────┬──────────┘
           │
      ┌────┴────┬──────────┐
      │          │          │
  ┌───▼──┐  ┌───▼──┐  ┌───▼──┐
  │Worker│  │Worker│  │Worker│  Выполняют
  │ #1   │  │ #2   │  │ #3   │  задачи
  └──────┘  └──────┘  └──────┘
   Process  Process   Process

Суть Celery

  1. Асинхронность — приложение отправляет задачу в очередь и сразу возвращает ответ
  2. Распределённость —워커ы могут быть на разных серверах
  3. Надёжность — задачи сохраняются в broker'е до выполнения
  4. Масштабируемость — добавляйте워커ов по мере необходимости

Пример: Отправка Email

Проблема: синхронное выполнение

from django.views.decorators.http import require_http_methods
from django.core.mail import send_mail

@require_http_methods(["POST"])
def register_user(request):
    user_email = request.POST.get('email')
    
    # Синхронная отправка email
    send_mail(
        'Добро пожаловать!',
        'Спасибо за регистрацию...',
        'noreply@example.com',
        [user_email],
        fail_silently=False,
    )
    # Отправка занимает 2-5 секунд
    # Пользователь ждёт ответа
    
    return HttpResponse('Зарегистрированы!')

Проблема: пользователь ждёт 5 секунд, пока письмо отправится.

Решение: асинхронное с Celery

# tasks.py
from celery import shared_task
from django.core.mail import send_mail

@shared_task
def send_welcome_email(user_email):
    """Задача для отправки письма"""
    send_mail(
        'Добро пожаловать!',
        'Спасибо за регистрацию...',
        'noreply@example.com',
        [user_email],
        fail_silently=False,
    )
    return f'Email sent to {user_email}'

# views.py
from django.http import HttpResponse
from .tasks import send_welcome_email

def register_user(request):
    user_email = request.POST.get('email')
    
    # Отправляем задачу в очередь
    send_welcome_email.delay(user_email)
    # Возвращаем ответ сразу! (~10ms)
    
    return HttpResponse('Зарегистрированы!')

Механика:

  1. send_welcome_email.delay(email) отправляет задачу в Redis/RabbitMQ
  2. Функция возвращает async result ID и сразу выходит
  3. На отдельном сервере worker получает задачу из очереди
  4. Worker выполняет send_welcome_email(email) в отдельном процессе
  5. Результат сохраняется

Celery vs Threading

Threading

import threading
import time

def send_email_thread(email):
    time.sleep(3)  # Отправка
    print(f'Отправили {email}')

# В Django view
thread = threading.Thread(target=send_email_thread, args=(user_email,))
thread.start()

Проблемы:

[PID 1000] Django процесс
  ├─ Main thread (Web запросы)
  ├─ Thread 1 (send_email) <- может упасть
  ├─ Thread 2 (send_email) <- может упасть
  └─ Thread 3 (send_email) <- может упасть

Если процесс 1000 упадёт — все потоки тоже упадут!
Все задачи потеряются!

Celery

[PID 1000] Django процесс
  └─ send_task() -> Redis

[PID 2000] Worker процесс #1 <- выполняет задачи
[PID 2001] Worker процесс #2 <- выполняет задачи  
[PID 2002] Worker процесс #3 <- выполняет задачи

Если Worker #1 упадёт — остальные продолжат.
Если Django упадёт — задачи останутся в Redis и выполнятся позже.

Типы выполнения Celery

1. Асинхронное (самое частое)

@shared_task
def long_running_task(param):
    # Выполняется где-то на worker'е
    return result

# В view - не ждём результата
long_running_task.delay(param)
return 'Задача принята'

2. С ожиданием результата (синхронное поведение)

# Ждём результат (timeout 10 секунд)
result = long_running_task.delay(param)
try:
    output = result.get(timeout=10)  # Блокирует поток
except TimeLimitExceeded:
    print('Задача не завершилась за 10 сек')

3. Scheduled (периодические задачи)

from celery.schedules import crontab

app.conf.beat_schedule = {
    'every-10-seconds': {
        'task': 'tasks.cleanup_old_sessions',
        'schedule': 10.0,
    },
    'every-morning-at-8am': {
        'task': 'tasks.send_daily_digest',
        'schedule': crontab(hour=8, minute=0),
    },
}

Когда использовать Celery

✅ Отправка email и SMS ✅ Обработка больших файлов ✅ Интеграция с внешними API (могут быть медленные) ✅ Периодические задачи (очистка БД, синхронизация) ✅ Обработка изображений, видео ✅ Сложные вычисления ✅ Когда нужна отказоустойчивость

❌ Очень быстрые операции (< 100ms) ❌ Простые веб-приложения без background job'ов ❌ Если нет надёжного message broker'а

Выводы

  • Celery НЕ выполняется в отдельном потоке — это отдельный процесс
  • Celery это распределённая система —워커ы могут быть на разных машинах
  • Celery это очередь задач — tasks хранятся в broker'е до выполнения
  • Celery обеспечивает надёжность — если worker упадёт, задача переведётся другому
  • Celery масштабируется — добавляйте worker'ов горизонтально

Этот подход позволяет создавать responsive веб-приложения, которые не блокируют пользователей на долгих операциях.