Asynchronous tasks no Django utilizando Celery

Olá,

no artigo de hoje vou mostrar de forma resumida, como criar Asynchronous tasks no Django utilizando Celery, uma ferramenta distribuída para gerenciamento de filas/tarefas. Com ele é possível que você execute tarefas em segundo plano, eliminando os gargalos causados pela execução síncrona.

Para ilustrar as vantagens da execução assíncrona, imagine que quando você enviasse um e-mail para um cliente, você ficasse parado, enquanto não recebesse a resposta. A execução das outras tarefas ficariam bloqueadas, esperando por algo que não está sob o seu controle e que pode demorar algum tempo para acontecer.

No modo assíncrono, você enviaria o e-mail e continuaria trabalhando enquanto espera pela resposta.

Quando executar tarefas de forma assíncrona?

  • Operações de longa-duração e que exigem grande poder de processamento (CPU)
  • Leitura/escrita de arquivos em disco (I/O)
  • Chamadas de serviços remotos (utilização de API’s terceiras, serviços na cloud, etc.)

Como o Celery funciona?

O Celery é formado basicamente por três componentes:

  • Task – a tarefa que deve ser executada
  • Broker – responsável por gerenciar as filas de tarefas que estão aguardando para serem executadas
  • Worker – responsável por executar as tarefas

Diagrama Celery

Como é possível ver no diagrama, pode-se utilizar o RabbitMQ ou Redis como message broker. Nesse artigo iremos utilizar o Redis, que é mais simples e fácil de instalar.

Instalando e configurando o Celery

A instalação do Celery é bem simples, consiste apenas de instalar alguns pacotes e adicionar variáveis ao settings.py de sua aplicação Django, considerando que o Redis já esteja instalado e rodando no localhost:6379.

Instale os pacotes utilizando o pip:

pip install celery[redis] django-celery South flower

Adicione as linhas abaixo ao arquivo settings.py:

import djcelery
djcelery.setup_loader()
BROKER_URL = 'redis://localhost:6379/0'
BROKER_BACKEND = 'redis'
CELERYBEAT_SCHEDULER = "djcelery.schedulers.DatabaseScheduler"

Adicione à variável INSTALLED_APPS o ‘djcelery’.

# Application definition
INSTALLED_APPS = (
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'djcelery',
    'SEU_APP',
)

Execute o comando migrate para que as tabelas do app djcelery sejam criadas no banco de dados.

python manage.py migrate djcelery

Definindo e executando tasks

Para definir que um método deve ser considerado uma task do Celery, devemos utilizar o decorator @task.

from celery.task import task

@task(name='taskSomar')
def taskSomar(x,y):
    return x+y

Pronto, agora para colocar a task na fila de execução podemos utilizar os métodos delay ou apply_async.

A principal diferença entre eles é que o delay coloca a task na fila para execução imediata ( assim que um worker estiver disponível), enquanto o apply_async recebe mais parâmetros, entre eles o countdown que define o tempo em que a task deve ser mantida na fila até que um worker a execute.

#Execução imediata
taskSomar.delay(1,1)

#Execução postergada em 60 segundos
taskSomar.apply_async(args=[1,1],countdown=60)

Para que  as tasks sejam executadas precisamos de um Worker. Para subir um processo de worker, execute o comando.

python manage.py celery worker -B -l INFO

Essa deve ser a mensagem exibida, mostrando o método taskSomar sendo reconhecido como uma task.
worker upLogo em seguida, o Worker já irá executar as tasks que estiverem na fila.task executada

Agendando execução de tasks

Para agendar uma task, acesse a interface de administração de Django e clique em Periodic Tasks.agendando tasksEscolha a task, o tempo de intervalo e  dê um label para o agendamento.agendando tasks 2 Pronto, sua task será executada dentro do intervalo escolhido, desde que você utilize a opção -B ao iniciar o processo do Worker.

Criando filas de execução

Legal, já sabemos como executar as tasks de maneira assíncrona, porém só temos uma fila de execução e nem todas tasks tem o mesmo grau de importância.

Para termos diferentes filas de execução, devemos adicionar as linha abaixo à configuração do Celery no arquivo settings.py.

from kombu import Exchange, Queue
default_exchange = Exchange('default', type='direct')
secundaria_exchange = Exchange('queue', type='direct')
CELERY_QUEUES = (
    Queue('default', default_exchange, routing_key='default'),
    Queue('secundaria', secundaria_exchange, routing_key='queue.secundaria'),
)
CELERY_DEFAULT_QUEUE = 'default'
CELERY_DEFAULT_EXCHANGE = 'default'
CELERY_DEFAULT_ROUTING_KEY = 'default'

Nesse exemplo, estamos definindo duas filas: a padrão (default) e a secundária.

Para definir à qual fila a taskSomar deve ser incluída, adicione as linhas:

CELERY_ROUTES = ({'taskSomar': {
                  'queue': 'secundaria',
                  'routing_key': 'queue.secundaria'
                 }},)

Dessa forma taskSomar será sempre incluída na fila secundária, deixando a fila default para tasks mais importantes.

Caso você deseje que essa escolha pela fila seja dinâmica, veja na documentação do Celery como definir uma classe Route que fará a escolha da fila de acordo com a lógica desejada.

Verificando a fila de execução

Algo muito importante durante o desenvolvimento das tasks é checar a saúde da filas, tempo de execução das tasks, etc.

Para isso podemos utilizar o Celery Flower, um monitor em tempo real muito bom. Para inicia-lo excute o comando:

python manage.py celery flower

flower up
A tela incial mostra o status dos workers.
flower worker

Clicando no Worker, você acessa diversos detalhes. Por exemplo as últimas tasks executadas.flower worker details

Na aba Monitor, você pode acompanhar gráficos em tempo real. Nessa figura abaixo, o tempo médio de execução e espera na fila.task_times

Número de tasks em cada fila.

task_broker

É isso ai, espero que esse artigo ajude quem esteja iniciando com o Celery.

Qualquer dúvida, comentário e/ou feedback é bem vindo. 🙂

Leave a Reply