Pular para conteúdo

Aula 18 - Tarefas em Segundo Plano com Celery e Redis 🐇

Objetivo Pedagógico

Objetivo: Arquitetura assíncrona de processamento em segundo plano (background jobs), filas de mensageria com Celery, agendamento de tarefas e retry com backoff.


📑 1. Fundamentos Teóricos & Análise Técnica

Tarefas de longa duração (como processamento e conversão de vídeos, treinamento de modelos de machine learning, geração de relatórios fiscais em PDF e disparo em lote de e-mails) nunca devem ser executadas síncronamente na thread da requisição HTTP. Reter a resposta por mais de alguns segundos exaure o pool de conexões do servidor e degrada severamente a experiência do usuário.

A solução arquitetural padrão na indústria Python consiste em desacoplar o produtor do consumidor via Filas de Mensagens (Task Queues) com Celery e Redis/RabbitMQ: 1. Produtor (Web API): Recebe a solicitação do usuário, despacha um evento de trabalho (job) para a fila com task.delay(*args) e retorna instantaneamente HTTP 202 Accepted com o ID da tarefa. 2. Message Broker (Redis/RabbitMQ): Armazena as mensagens em memória de forma ordenada e distribuída. 3. Workers (Trabalhadores Celery): Processos em nós independentes que retiram mensagens da fila e executam a computação pesada em background. 4. Result Backend: Armazenamento opcional para consulta posterior do status e resultado da execução.

📐 Arquitetura Conceitual & Diagrama de Fluxo

sequenceDiagram
    autonumber
    actor Client as Cliente Web
    participant API as FastAPI Web Server
    participant Broker as Redis Message Broker
    participant Worker as Celery Worker Process

    Client->>API: POST /reports/generate
    API->>Broker: Despacha Mensagem (task.delay)
    API-->>Client: 202 Accepted (job_id: "xyz-123")
    Broker->>Worker: Entrega Mensagem para Execução
    Note over Worker: Processamento Pesado em Segundo Plano (15s)
    Worker->>Broker: Atualiza Status: "SUCCESS"
    Client->>API: GET /reports/status/xyz-123
    API-->>Client: Retorna Relatório Concluído!

🔍 Pilares e Diretrizes Técnicas

Nesta unidade, aprofundamos os seguintes conceitos fundamentais: - Desacoplamento Assíncrono: O servidor web responde em milissegundos enquanto a tarefa consome os minutos necessários no worker. - Políticas de Retry Automático: Configuração de tentativas com backoff exponencial para lidar com falhas transitórias de rede. - Limitação de Concorrência: Controle fino do número de processos filhos (--concurrency) para não esgotar a RAM do servidor. - Tarefas Agendadas com Celery Beat: Substituto profissional do Cron para rotinas periódicas de limpeza e consolidação noturna.


🛠️ 2. Implementação Prática em Python, Celery e Redis Message Broker

Abaixo está a implementação técnica de referência, estruturada com padrões de engenharia de software e foco em robustez:

// celery_tasks.py (Definição de Tarefas com Retry e Backoff)
from celery import Celery
import time

# Configuração do Celery com Redis como Broker e Backend
celery_app = Celery(
    "tasks",
    broker="redis://localhost:6379/0",
    backend="redis://localhost:6379/1"
)

@celery_app.task(
    bind=True,
    max_retries=3,
    default_retry_delay=5,
    autoretry_for=(Exception,),
    retry_backoff=True
)
def process_pdf_report(self, user_id: str, report_data: dict) -> dict:
    try:
        print(f"[Worker] Iniciando geração de PDF para usuário: {user_id}")
        time.sleep(10) # Simula renderização pesada
        print(f"[Worker] PDF gerado com sucesso!")
        return {"user_id": user_id, "status": "COMPLETED", "file_url": f"/files/{user_id}.pdf"}
    except Exception as exc:
        print(f"[Worker] Falha temporária. Agendando retry: {exc}")
        raise self.retry(exc=exc)

💡 Análise Passo a Passo do Código

  1. Parâmetro bind=True: Permite que a função acesse a instância da própria tarefa (self) para inspecionar tentativas e disparar retries.
  2. Retry Backoff Exponencial: retry_backoff=True dobra o tempo de espera a cada nova tentativa (5s, 10s, 20s), evitando sobrecarregar serviços externos.
  3. Result Backend Dedicado: A URL redis://localhost:6379/1 armazena o dicionário retornado para consulta posterior pela API.

🎯 3. Próximos Passos & Sequência Didática