{"id":628,"date":"2016-11-10T17:00:00","date_gmt":"2016-11-10T17:00:00","guid":{"rendered":"https:\/\/geoadmin.com.br\/?p=628"},"modified":"2021-06-09T10:25:23","modified_gmt":"2021-06-09T13:25:23","slug":"celery-python","status":"publish","type":"post","link":"https:\/\/geoadmin.com.br\/?p=628","title":{"rendered":"Celery e Tarefas ass\u00edcronas com Python"},"content":{"rendered":"<p>O <a href=\"http:\/\/celeryproject.org\">Celery<\/a> \u00e9 um aplica\u00e7\u00e3o, escrita em Python, que permite que voc\u00ea delegue tarefas a diversos workers, localizados em outros servidores.<\/p>\n<p>O <a href=\"http:\/\/celeryproject.org\">Celery<\/a> \u00e9 uma forma bastante de realizar estes <strong>jobs<\/strong> ou <strong>tarefas<\/strong> de forma ass\u00edncrona, sem ter de quebrar muito a cabe\u00e7a com conceitos complicados e dif\u00edceis de serem acertados.<\/p>\n<p>Primeiramente, vou explicar como o <a href=\"http:\/\/celeryproject.org\">Celery<\/a> funciona, em termos de arquitetura.<\/p>\n<p>Temos alguns componentes principais envolvidos:<\/p>\n<ol>\n<li>Aplica\u00e7\u00e3o &#8211; este carinha aqui \u00e9 a sua aplica\u00e7\u00e3o normal, a qual o usu\u00e1rio ou outra m\u00e1quina interage;<\/li>\n<li>Broker &#8211; este componente \u00e9 uma fila de mensagens, utilizada para transportar mensagens entre os processos que estamos descrevendo aqui. O Celery permite o uso de diversos brokers, como <a href=\"http:\/\/redis.io\/\">redis<\/a> e <a href=\"https:\/\/www.rabbitmq.com\/\">RabbitMQ<\/a>. Usamos o <a href=\"http:\/\/redis.io\/\">redis<\/a> por aqui, mas \u00e9 uma prefer\u00eancia;<\/li>\n<li>Worker &#8211; este \u00e9 o componente que trabalha de verdade. Qualquer tarefa agendada pela aplica\u00e7\u00e3o no broker, ser\u00e1 executada pelo worker;<\/li>\n<li>Beat &#8211; \u00e9 um componente opcional, mas funciona como um agendador de tarefas, que dispara de tempos em tempos as tarefas para o broker, como uma aplica\u00e7\u00e3o;<\/li>\n<\/ol>\n<p>Com esta arquitetura, \u00e9 bastante f\u00e1cil escalar a coisa toda para funcionar em v\u00e1rias m\u00e1quinas, bastando adicionar mais workers conforme a frequ\u00eancia das tarefas aumentam. O <a href=\"http:\/\/celeryproject.org\">Celery<\/a> \u00e9 muito customiz\u00e1vel e tem op\u00e7\u00f5es importantes, como controle de filas (algumas tarefas s\u00f3 podem ser processadas uma a uma, em uma \u00fanica fila, sem concorr\u00eancia alguma, enquanto outras podem ser paralelizadas), aplica\u00e7oes de monitoramento (como o <a href=\"http:\/\/flower.readthedocs.io\/en\/latest\/\">Flower<\/a>) e extens\u00f5es de biblioteca para os desenvolvedores (como o <a href=\"https:\/\/policystat.github.io\/jobtastic\/\">Jobtastic<\/a>).<\/p>\n<p>Vamos hoje dar um exemplo bastante simples, integrado a um projeto Django. N\u00e3o vamos entrar em muitos detalhes de como subir os workers ou o broker, pois vai depender muito do seu ambiente.<\/p>\n<p>Imagine que sua aplica\u00e7\u00e3o manda emails. Quase todas as aplica\u00e7\u00f5es (web) mandam emails, mas algumas mandam mais emails que outras e com maior frequ\u00eancia. Entre o ponto em que uma requisi\u00e7\u00e3o feita pelo usu\u00e1rio \u00e9 processada e uma resposta \u00e9 retorna, v\u00e1rios segundos podem se passar e n\u00e3o queremos deixar o usu\u00e1rio esperando. Ent\u00e3o decidimos delegar o envio do email, que \u00e9 mais demorado, para uma tarefa ass\u00edcrona no Celery.<\/p>\n<p>Como fazer isto?<\/p>\n<p>Bem, primeiramente \u00e9 necess\u00e1rio configurar sua aplica\u00e7\u00e3o Celery. Fazemos (aqui na SIGMA) por meio de um arquivo celery.py, localizado junto <code>wsgi.py<\/code> do projeto. Ele \u00e9 mais ou menos assim:<\/p>\n<pre><code># coding: utf-8\n\"\"\"Configura\u00e7\u00e3o inicial do Celery\"\"\"\nfrom __future__ import absolute_import\nimport os\nfrom django.conf import settings\n\nif not \"DJANGO_SETTINGS_MODULE\" in os.environ:\n    os.environ[\"DJANGO_SETTINGS_MODULE\"] = \"settings.local\"  # aqui na SIGMA usamos settings separados para cada ambiente\n\nfrom celery import Celery\napp = Celery('nome-da-app')\n\napp.config_from_object('django.conf:settings')\napp.autodiscover_tasks(lambda: settings.INSTALLED_APPS)\n<\/code><\/pre>\n<p>Com esta configura\u00e7\u00e3o batuta, podemos iniciar um worker, por exemplo. Al\u00e9m disso, usamos o autodiscover do <a href=\"http:\/\/celeryproject.org\">Celery<\/a>, que percorrer\u00e1 todas as <code>INSTALLED_APPS<\/code> e cadastrar\u00e1 todas as tarefas dentro dos m\u00f3dulos <code>tasks.py<\/code>.<\/p>\n<p>Ok, com o Celery configurado, podemos startar um worker.<\/p>\n<pre><code>celery -A projeto worker -l info\n<\/code><\/pre>\n<p>Agora, vamos criar nossa task e chamar ela de dentro de uma view qualquer. Crie um arquivo chamado <code>tasks.py<\/code>em uma de suas apps, por exemplo, sistema.<\/p>\n<p>Para definir a task, utilizaremos um decorador:<\/p>\n<pre><code>from projeto.celery import app  # lembre do nosso arquivo celery.py?\n\n\n@app.task()\n def email_async(email, assunto, template, contexto):\n    template_html = get_template(template)\n    template_txt = get_template(template.replace('.html', '.txt'))\n    if isinstance(contexto, dict):\n        contexto = Context(contexto)\n\n    contexto['email_header_image'] = get_random_image()\n    html = template_html.render(contexto)\n    txt = template_txt.render(contexto)\n    if isinstance(email, basestring):\n        email =[email]\n\n    send_mail(assunto,\n              txt,\n              'remetente',\n              recipient_list=email,\n              html_message=html)\n<\/code><\/pre>\n<p>Com a tarefa definida, podemos invoc\u00e1-la de forma ass\u00edcrona. O <a href=\"http:\/\/celeryproject.org\">Celery<\/a> tem <strong>muitas<\/strong> formas de se invocar uma tarefa. Existe um neg\u00f3cio chamado canvas que permite voc\u00ea combinar as tarefas de forma ass\u00edcrona, paralelas, etc. Confira a <a href=\"http:\/\/docs.celeryproject.org\/en\/master\/userguide\/canvas.html\">documenta\u00e7\u00e3o do canvas<\/a>.<\/p>\n<p>V\u00e1 em uma de suas views que enviam emails:<\/p>\n<pre><code>from .tasks import email_async\n\ndef view_qualquer(request):\n\n    # fa\u00e7a alguma coisa \u00fatil\n\n    email_async.delay('novo email', 'sistema\/email.html', {'variavel_contexo_a': 'foo', 'b': 'novo email maroto'})\n<\/code><\/pre>\n<p>O segredo da chamada ass\u00edcrona est\u00e1 no m\u00e9todo delay, que ir\u00e1 usar o broker para agendar a tarefa e futuramente ser\u00e1 executada por um worker.<\/p>\n<p>\u00c9 um resumo muito curto para mostrar todas as capacidades do Celery, mas \u00e9 um projeto muito bem mantido, utilizado por <strong>muita<\/strong> gente.<\/p>\n<p>O exemplo que demos, apesar de parecer bobo, \u00e9 bastante significativo quando estamos falando de uma aplica\u00e7\u00e3o que envia centenas ou milhares de emails por dia, diminuindo muito o impacto nos servidores de aplica\u00e7\u00e3o e permitindo que voc\u00ea lide com uma carga bem mais alta.<\/p>\n<p>Iremos fazer uma pequena s\u00e9rie sobre o <a href=\"http:\/\/celeryproject.org\">Celery<\/a>. Tem jeito de consultar o status da tarefa, agendar tarefas peri\u00f3dicas, controlar exce\u00e7\u00f5es e um monte de coisa legal.<\/p>\n<p>Fique ligado e obrigado!<\/p>\n","protected":false},"excerpt":{"rendered":"<p>O Celery \u00e9 um aplica\u00e7\u00e3o, escrita em Python, que permite que voc\u00ea delegue tarefas a diversos workers, localizados em outros servidores. O Celery \u00e9 uma forma bastante de realizar estes jobs ou tarefas de forma ass\u00edncrona, sem ter de quebrar muito a cabe\u00e7a com conceitos complicados e dif\u00edceis de serem acertados. Primeiramente, vou explicar como &hellip; <\/p>\n<p class=\"link-more\"><a href=\"https:\/\/geoadmin.com.br\/?p=628\" class=\"more-link\">Leia mais<span class=\"screen-reader-text\"> &#8220;Celery e Tarefas ass\u00edcronas com Python&#8221;<\/span><\/a><\/p>\n","protected":false},"author":4,"featured_media":0,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_feature_clip_id":0,"_jetpack_memberships_contains_paid_content":false,"footnotes":"","jetpack_post_was_ever_published":false},"categories":[1],"tags":[57,30,29,56,45],"class_list":["post-628","post","type-post","status-publish","format-standard","hentry","category-sem-categoria","tag-async","tag-desenvolvimento-de-sistemas","tag-dev","tag-devops","tag-python"],"jetpack_sharing_enabled":true,"jetpack_featured_media_url":"","_links":{"self":[{"href":"https:\/\/geoadmin.com.br\/index.php?rest_route=\/wp\/v2\/posts\/628","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/geoadmin.com.br\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/geoadmin.com.br\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/geoadmin.com.br\/index.php?rest_route=\/wp\/v2\/users\/4"}],"replies":[{"embeddable":true,"href":"https:\/\/geoadmin.com.br\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=628"}],"version-history":[{"count":1,"href":"https:\/\/geoadmin.com.br\/index.php?rest_route=\/wp\/v2\/posts\/628\/revisions"}],"predecessor-version":[{"id":1325,"href":"https:\/\/geoadmin.com.br\/index.php?rest_route=\/wp\/v2\/posts\/628\/revisions\/1325"}],"wp:attachment":[{"href":"https:\/\/geoadmin.com.br\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=628"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/geoadmin.com.br\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=628"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/geoadmin.com.br\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=628"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}