Tarefas demoradas de forma assíncrona com Django e Celery

Estou escrevendo esse post empolgado por uma lightning talk que fiz no GruPy-SP (16/07). Assim aproveito pra explicar um pouco mais o que estou dizendo nos slides.

O problema de tarefas demoradas do lado do servidor

Sempre que o cliente faz uma requisição web (request), o servidor faz um processamento, normalmente pra quem usa o django, ele lê a requisição, trata os dados recebidos, salva ou recupera registros do banco de dados (através dos models), faz algum processamento do que será exibido para o usuário, renderiza isso em um template e manda uma resposta (response) para o cliente.

Dependendo da tarefa que você executa no servidor a resposta pode demorar muito e isso leva à problemas de TimeOut, a experiência do usuário fica comprometida (quanto mais o servidor demorar pra responder maior a chance do usuário sair do seu site) e também pode acontecer da lentidão ser causada por outros, como por exemplo o uso de uma API externa.

Existem diversas tarefas que podem demorar pra ser executadas. Se você tiver que criar um relatório pesado, acionado por um client web. Se de repente você precisar enviar diferentes emails para uma lista ou por exemplo editar um vídeo depois que o usuário faz o upload na sua página.

Caso real

Esse é um problema que me deparei um dia na geração de um relatório. O relatório levava cerca de 20 minutos para ser gerado, o cliente recebia um erro de timeout e obviamente não dá pra esperar 20 minutos pra gerar alguma coisa. Pra driblar isso e deixar a tarefa em background, alguém resolveu usar um comando do sistema operacional. (No linux se você colocar & no final de um comando esse comando roda em background).

django-view-os-system

Note que em seguida o django envia mostra uma mensagem ao usuário de que ele será avisado por e-mail ao final da geração do relatório.
Alguma coisa não parece certa aqui.

Me arrependo imediatamente dessa decisão

Celery – A solução para esses problemas!

O Celery é um gerenciador de tarefas assíncronas. Com ele você pode executar uma fila de tarefas (que ele recebe por meio de mensagens), pode agendar tarefas direto no seu projeto sem precisar do cron e ele ainda tem integração fácil com a maioria dos frameworks python mais utilizados (django, flask, tornado, etc.)

Como o Celery funciona

Essa imagem kibada da internet dá uma visão geral de como fica o fluxo da sua aplicação.

estrutura-celery

  • O User (ou Client ou Producer) é a sua aplicação Django.
  • O AMPQ Broker é um Message Broker. Um programa responsável por manter a fila de mensagens que serão trocadas entre o seu programa e o Celery, geralmente é o RabbitMQ ou o Redis
  • Os workers (ou consumers) que vão executar as tarefas que você quer que sejam assíncronas.
  • O Result Store, que é onde os workers podem ou não salvar os resultados das tarefas que foram executadas.

O Client pode passar uma tarefa, uma lista de tarefas, tarefas periódicas ou o retry de alguma tarefa pra fila do Message Broker. O Message Broker distribui essas tarefas entre os workers e o resultado dessas tarefas pode ser escrito em um Result Store (Memcahed, RDBMS, MongoDB, etc…) que mais tarde pode ser lido pelo Client novamente.

Qual Broker utilizar?

O recomendado pelo Celery é o RabbitMQ.

Instalando e configurando o RabbitMQ

Existem vários exemplos de como utilizar o Redis, eu vou mostrar como usar o RabbitMQ.

Você só precisa:

  1. Instalar o RabbitMQ (Baixar o pacote no site ou adicionar o repo deles no sources.list e instalar com APT-GET)
    sudo apt-get install rabbitmq-server
  2. Criar um usuário, um virtual host e dar permissões para esse usuário no virtualhost:
    
    sudo rabbitmqctl add_user myuser mypassword
    sudo rabbitmqctl add_vhost myvhost
    sudo rabbitmqctl set_permissions -p myvhost myuser ".*" ".*" ".*"
    

Instalando e configurando o Celery

pip install celery

No seu settings.py:

#Celery Config
BROKER_URL = 'amqp://guest:[email protected]:5672//'
CELERY_ACCEPT_CONTENT = ['json']
CELERY_TASK_SERIALIZER = 'json'
CELERY_RESULT_SERIALIZER = 'json'

Na pasta do projeto (a mesma pasta do settings.py) crie um arquivo chamado celery.py:


from __future__ import absolute_import

import os
from celery import Celery
from django.conf import settings

# set the default Django settings module for the 'celery' program.
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'nome_do_proj.settings')

app = Celery('nome_do_proj')

# Using a string here means the worker will not have to
# pickle the object when using Windows.
app.config_from_object('django.conf:settings')
app.autodiscover_tasks(lambda: settings.INSTALLED_APPS)

Esse autodiscover_tasks permite que seu projeto ache todas as tarefas assíncronas de cada app instalada nele. Nessa mesma pasta do settings.py, você vai modificar o arquivo de pacote __init__.py:


from __future__ import absolute_import

# This will make sure the app is always imported when
# Django starts so that shared_task will use this app.
from .celery import app as celery_app

Criando as tarefas assíncronas do seu app

Na pasta do seu app crie um arquivo chamado tasks.py:

from __future__ import absolute_import
from celery import shared_task
from relatorios import gerar_relatorio_excel


@shared_task
def gerar_relatorio(data_inicial, data_final, email):
    gerar_relatorio_excel(
        data_inicial = data_inicial,
        data_final = data_final,
        email = email
    )
    return "Relatorio Excel gerado"

Agora é só fazer importar essa função que foi criada usando o método delay que foi adicionado à sua função pelo decorator shared_task.

tabelao assincrono

Rodando o Celery

Agora você precisa rodar o celery. Ele vai rodar os seus workers pra executar suas tarefas de forma assíncrona. Por padrão ele inicia um worker para cada núcleo de processador, mas você pode determinar quantos com -c Numero_de_workers

celery --app=nome_projeto worker --loglevel=INFO

E então você verá essa tela:

celery-rodando

Você pode então testar sua função assíncrona com o shell do django mesmo:

chama funcao

E o que aparece na tela que está mostrando o celery:

aparece no celery

Colocando o Celery em Produção

Para usar o Celery em produção eu recomendo que você use o Supervisor

Para instalar o supervisor:

sudo apt-get install supervisor

Depois crie um arquivo de configuração para o celery e coloque um link simbólico para esse arquivo no /etc/supervisor/conf.d/

[program:celery]
command=/home/deploy/.virtualenvs/meu_virtual_env/bin/celery --app=nome_proj worker --loglevel=INFO
directory=/home/deploy/webapps/projeto_django
user=nobody
autostart=true
autorestart=true
redirect_stderr=true

Para o supervisor ler novamente os arquivos que estão em sua pasta de configuração:

sudo supervisorctl reread
sudo supervisorctl update

Agora é só iniciar o celery:

sudo supervisorctl start celery

Bom, acho que é só isso. Esses são os slides da minha apresentação:

Continuação

Celery com múltiplas filas, retry e tarefas agendadas

Fontes

E aqui vão as fontes de pesquisa (ou algumas) que eu utilizei nesse processo:

Bônus:

Como o Feed do Instagram usa o Celery e o RabbitMQ

Um pequeno prazer de uma startup que não deu certo.

 

Quem nunca teve uma empresa que não deu certo, que atire a primeira pedra.

Sempre gostei de empreendedorismo. Eu gostava de ler matérias sobre empresas, gostava de ir nas empresas dos meus pais e gostava dos filmes dos anos 80 estilo Jerry Maguire. Minha família sempre foi empreendedora. Meu pai teve de loja de material de aquarismo até distribuidora de salgadinho de bar, meu tio construiu a maior rede de salões de beleza da zona norte e até hoje não se passa uma semana sem que minha mãe me fale uma ideia nova que ela teve (Se eu publicar alguma, ela me mata). O fato é que o empreendedorismo está no meu sangue.

No final de 2011, eu e meu amigo Daniel resolvemos montar uma startup. Na ocasião eu tinha um site que revendia instrumentos musicais. Nada muito formal, mas era uma escola pra mim. Estava ganhando dinheiro, me mantendo, tive a oportunidade de largar um emprego público e fazer mais dinheiro em casa de cueca do que indo perfumado até o prédio da FEA. Falar sobre a Apoio Musical levaria até mais de um post sobre os 2 anos que eu a mantive no ar e ela me manteve.

Bom, montamos essa startup porque estávamos vidrados em Crowdsourcing. Queríamos muito, que o crowdsourcing fosse um jeito de dar às empresas a oportunidade de gastar pouco pra ter ideias de qualidade e de dar às pessoas a oportunidade de trabalhar em projetos de grandes empresa, mostrando seu potencial.

 

O primeiro problema que enfrentaríamos era o do ovo e o da galinha: como teríamos empresas sem ter pessoas interessadas em anunciar e como teríamos pessoas pra participar dos desafios sem ter empresas com desafios?

Enfim, resolvemos começar buscando às pessoas. (O que hoje eu acredito que não foi a melhor estratégia).

Pra alcançar essas pessoas nós resolvemos criar um Desafio com Ideias que pudessem melhorar a cidade de São Paulo. Chamamos isso de Desafio São Paulo.

Criamos um aplicativo para o facebook pra poder receber as ideias, já que ainda não tínhamos uma plataforma criada e não me lembro como alguém gostou do desafio e ele foi parar no Catraca Livre.

ideias-na-mesa-catraca-livre

Isso foi o suficiente pra várias pessoas entrarem no Desafio. Nisso tivemos um outro problema: Como escolher qual ideia é a melhor? E nisso, a CAOS Focado que é uma empresa de consultoria nos ajudou criando um método objetivo pra definir quais eram as melhores ideias (eu deveria ter filmado pra mostrar quão genial foi o Miguel Chaves resolvendo isso pra gente).

A startup como você já previu no começo do texto, escafedeu-se, fizemos o desafio, não conseguimos fechar com nenhuma outra empresa um desafio sequer, apesar de negociar durante meses com uma empresa grande da área de turismo. Então o que eu estou comemorando aqui como um pequeno prazer? Olhe as ideias escolhidas como as melhores para São Paulo no nosso desafio e pense no que mudou em São Paulo de 2012 para 2015.

  • Ciclo Faixas com acesso a CPTM nas marginais
  • Menos vagas de rua para carros
  • Ruas de lazer aos domingos
  • Transporte Coletivo 24 horas
  • WiFi gratuito em locais públicos

Isso tudo começou a mudar em São Paulo e vai continuar mudando porque agora já é tendência. Se quiser ter certeza, confere meu post original de quando eu publiquei o desafio em março de 2012:

 

http://blog.ideiasnamesa.com.br/desfile-das-campeas-desafio-sao-paulo/