Procrastinando com Celery



.




adriano petrich petrich@gmail.com #TDC2011
O que é o Celery?
Ponto forte do Celery
 • Gerenciador de tarefas assíncrono
Ponto forte do Celery
    • Gerenciador      de          tarefas
      assíncrono
    • Distribuído entre máquinas
Os dois pontos fortes
do Celery
    • Gerenciador      de          tarefas
      assíncrono
    • Distribuído entre máquinas
Os dois pontos fortes
do Celery
    • Gerenciador      de          tarefas
      assíncrono
    • Distribuído entre máquinas
    • Agendador
Celery
Dentre os pontos fortes do Celery
destacam-se

      • Gerenciador     de        tarefas
        assíncrono
      • Distribuído entre máquinas
      • Agendador
      • Com tolerância a falhas
Como Funciona?
Resposta Curta
.
Resposta Longa
Estrutura
Tarefa
Publicadores
Fila
Trabalhadores/Consumidores
Tarefa
Tarefa
from celery.task import task

@task
def foo(val):
    return val
Publicador
Publicadores
Sincronamente

 >>> foo(5)
 5
Publicadores
Assincronamente

 >>> res = foo.delay(5)
Dois Jeitos
# não bloqueia
>>> res.ready()
True ou False
>>> res.result
5
# ou bloqueia
>>> res.get()
5
Fila
Fila
Backend sugerido é o RabbitMQ
(todas as crianças legais do bairro usam
erlang)
Mas também suporta
Redis
Beanstalk
MongoDB
CouchDB
e bancos suportados pelo ORM do Django
Trabalhador
Trabalhador
Pode estar rodando em outra máquina
Cada Worker process ainda pode usar
vários threads
Trabalhador
$ celeryd

$ ./manage celeryd
Por que?
Web ou StandAlone?
Web
Painel de Escalabilidade (djangocon.eu)
http://2011.djangocon.eu/talks/47/
Problemas comuns
What are the common mistakes you see.
--------------------------------------
Things that take a long time ("sending an email")
that lock up a process. Using the filesystem for caching.
Testing locally and pushing it live and see it fall over.
Using an external API can also take a long time.
Work with timeouts to work around it.
Processos demorados
entopem os tubes
Soluções comuns
Tenha um celery de criança:
Scaling wise, what should you think about before scaling
becomes an actual issue? And what should you definitely
leave until the moment comes you need to scale?
----------------------------------------------

First things first: use a queue like   celery, even on small sites.
Get used to such a queue and have it   in place, that'll help a
lot with performance later.Make sure   you've got your database
schema is mostly OK. It doesn't have   to be perfect right away,
but at least mostly OK.

Expect something to change and assume you'll have to swap something
out later to improve the performance.
email.py
from celery.task import task
from django.core.mail import send_mail

@task
def send(subject, message, recipient_list,
                from_email=None, **kwargs):
    if subject[0] != "[":
        subject = "[projeto] "+ subject
    if from_email is None:
        from_email = "suporte@fuuuuuu.com"
    send_mail(subject, message, from_email,
                    recipient_list, **kwargs)
Um bom padrão
from email import send

@task
def relatorio(dados):
    ....gera relatorio....
    send.delay("Relatorio Pronto",
        """
        Pode baixadas daqui:
        http://fuuuuuu.com/relatorios/%s.pdf
        """% filename,
        (email,))
Stand
Alone
Scripts
Map Reduce
 tarefas = []
 livros =["iliada.txt","odisseia.txt"....]
 for filename in livros:
     tarefas.append(
         conta_palavras.delay(filename)
         )


para pegar o resultado
if all([i.ready() for i in tarefas]):
    palavras = sum([i.result for i in tarefas])
Armadilhas e dicas
Django-celery no
admin
Inicia um worker
$ ./manage.py celeryd -l info -E
e daí: BAM!
Tem que rolar também
$ ./manage.py celerycam
Testar
#no settings.py
TEST_RUNNER='djcelery.contrib.test_runner.run_tests'

from django.test import TestCase
from myapp.tasks import add

class AddTestCase(TestCase):

   def testNoError(self):
       """Test that the ``add`` task runs
       with no errors,
       and returns the correct result."""
       result = add.delay(8, 8)

        self.assertEquals(result.get(), 16)
        self.assertTrue(result.successful())
Sem resposta
CELERY_IGNORE_RESULT = True
Transactions no
django
def bar(request):
    sanduiche = Sanduiche("Pernil")
    sanduiche.save()
    avisa_novo.delay(sanduiche.id)
Especialmente se
MIDDLEWARE_CLASSES = (
    'django.middleware.transaction.TransactionMiddleware',
    ...
Assim
def bar(request):
    sanduiche = Sanduiche("Pernil")
    sanduiche.save()
    if transaction.is_managed():
        transaction.commit()
    avisa_novo.delay(sanduiche.id)
Para desenvolvimento
from socket import error

try:
    avisa_novo.delay(sanduiche.id)
except error:
    log.error("conexao recusada")
    if not DEBUG:
        raise error
Muitas tarefas
for i in algo_grande:
    avisa_novo.delay(i)
Muitas tarefas
subtasks = []
for i in algo_grande:
    subtasks.append(subtask(avisa_novo, args=(i)))

TaskSet(tasks=subtasks).apply_async()
Créditos
http://www.flickr.com/photos/lexnger/1794462309
http://www.flickr.com/photos/atmtx/4250159996
http://www.flickr.com/photos/prettydaisies/2368005479
http://www.flickr.com/photos/benobryan/3766107097
http://www.flickr.com/photos/eli_k_hayasaka/3989808211
http://www.flickr.com/photos/carolinabarmell/2980660350
http://www.flickr.com/photos/jjay/2415455625
http://www.flickr.com/photos/tim_proffitt_white/3181616233
http://www.flickr.com/photos/daniele_sartori/4484430735
http://www.flickr.com/photos/project-404/27730644
Dúvidas?
@fractal
+Adriano Petrich
[codando.com.br, sfp.adrianopetrich.com,
blog.adrianopetrich.com]
Deixa para depois, Procrastinando com Celery em Python

Deixa para depois, Procrastinando com Celery em Python