Celery
O Celery é um Python Package Index (PyPi) que pode ser instalado via pip. Com ele conseguimos executar tarefas(tasks) em segundo plano e assim não bloqueamos nosso programa em execução, melhorando assim a experiência do usuário, que não precisa ficar esperando determinada tarefa terminar, como por exemplo um envio de email que muitas vezes é um pouco demorado.
Importante: Antes de instalar o Celery crie um virtualenv, para instalar o virtualenv clique aqui
1 2 |
$ virtualenv --no-site-packages myvenv $ source venv/bin/activate |
Para instalar o Celery digite:
1 |
$ pip install celery |
RabbitMQ
Celery precisa de um message broker para funcionar. O broker é um intermediário que gerencia a fila de tarefas e as envia para os Celery workes executá-las. Para instalar o RabbitMQ basta digitar:
1 |
$ sudo apt-get install rabbitmq-server |
Importante: Após a instalação o serviço RabbitMQ é iniciado automaticamente.
Os dois comandos abaixo não são necessários neste tutorial, use-os caso seu serviço RabbitMQ precise ser inicializado ou parado manualmente:
Inicia o serviço em background:
1 |
$ sudo rabbitmq-server -detached |
Nunca use Kill para parar o RabbitMQ, invés disso, use o comando abaixo:
1 |
sudo rabbitmqctl stop |
Abaixo uma imagem simplista de como funciona a fila de tarefas que serão executadas.
Dica: se achar necessário crie uma pasta chamada celery_hello por exemplo, apenas para para isolar nossos testes:
1 2 |
$ mkdir cellery_hello $ cd cellery_hello |
Agora crie um arquivo chamado myapp.py e cole o conteúdo abaixo:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
from celery import Celery app = Celery( 'myapp', broker='amqp://guest@localhost//', backend='amqp' ) @app.task(ignore_result=True) def print_hello(): print 'olá celery' return 'nao passa nada para o backend' @app.task def numeros_primos(x): multiples = [] results = [] for i in xrange(2, x+1): if i not in multiples: results.append(i) for j in xrange(i*i, x+1, i): multiples.append(j) return results @app.task def add(x, y): return x + y if __name__ == '__main__': app.start() |
Nas 7 primeiras linhas nós configuramos nossa app Celery, passando o nome ‘myapp’, o broker que é nosso gerenciador de mensagens e também um backend que é onde as respostas de nossas tarefas serão armazenadas, existem muitos outros backends como por exemplo: SQLAlchemy/Django ORM, Memcached, Redis, AMQP (RabbitMQ), e MongoDB.
1 2 3 4 5 6 7 |
from celery import Celery app = Celery( 'myapp', broker='amqp://guest@localhost//', backend='amqp' ) |
A função print_hello() não armazena nenhum resultado pois tem como objetivo apenas imprimir na tela ‘olá celery’, por isso passamos o parametro ignore_result para a task.
1 2 3 4 |
@app.task(ignore_result=True) def print_hello(): print 'olá celery' return 'nao passa nada para o backend' |
A função numeros_primos() e add() terão seus resultados armazenados no backend e poderão ter seus respectivos retornos consultados pelo método .get() que usaremos logo abaixo.
Agora vamos testar nossas tasks:
Abra duas janelas(dois consoles) e na primeira digite:
1 |
$ celery -A myapp worker -l info |
Na segunda tela digite:
1 2 3 |
$ python >>> from myapp import print_hello, numeros_primos, add >>> print_hello.delay().get() |
Percebe-se que nada é impresso na tela, isso por causa da propriedade ignore_result=True, mas acredite ela foi executada em background,
Agora vamos rodar a task add:
1 2 |
>>> add.delay(10, 15).get() >>> 25 |
Repare que neste caso, teve uma saída, isso porque o resultado ficou armazenado no backend e pode ser consultado através do .get()
Agora vamos rodar a task numeros_primos() que vai nos possibilitar descobrir alguns novos recursos do Celery:
1 |
>>> primos = numeros_primos(20000) |
Após executar a linha acima a console ficará “travada” (por pouco tempo) e você não conseguirá fazer mais nada até que a função seja executada por completo. Com isso percebemos o porque utilizar o Celery, pois com ele conseguimos executar a função e ainda podemos continuar executando outros comandos, pois a task numeros_primos está sendo executada em segundo plano, para testarmos isso, rode os comandos abaixo:
1 2 3 |
>>> primos = numeros_primos.delay(20000) >>> primos.ready() >>> False |
Agora aguarde um pouco e digite primos.ready() novamente e você receberá True como resposta. Digite também primos.get() para ver todos os números primos calculados:
1 2 3 |
>>> primos.ready() >>> True >>> primos.get() |
Conclusão
Celery é uma ótimo opção para aplicativos que precisam de performance e não podem ficar esperando a execução de tarefas. Com isso conseguimos melhorar a experiência dos usuários.
Abaixo alguns links onde me inspirei para escrever este post:
http://docs.celeryproject.org/en/latest/getting-started/first-steps-with-celery.html
https://www.digitalocean.com/community/tutorials/how-to-use-celery-with-rabbitmq-to-queue-tasks-on-an-ubuntu-vps
http://docs.celeryproject.org/en/latest/getting-started/brokers/rabbitmq.html#broker-rabbitmq