Ferramentas & Produtividade

Mensageria Assíncrona na Prática

8 min de leitura

Mensageria Assíncrona na Prática

O que é Mensageria Assíncrona e Por Que Importa Mensageria assíncrona é um padrão arquitetural onde componentes de um sistema se comunicam através de mensagens, sem esperar pela resposta imediata. Em vez de uma chamada síncrona (requisição-resposta), um serviço envia uma mensagem a uma fila e continua sua execução. Outro serviço processa essa mensagem quando estiver pronto. Isso desacopla completamente os sistemas, permitindo escalabilidade, resiliência e melhor distribuição de carga. Na prática, você utiliza mensageria quando precisa processar tarefas pesadas em background, sincronizar dados entre microserviços, ou lidar com picos de tráfego sem sobrecarregar um único servidor. A diferença com chamadas síncronas é clara: se um serviço falha em uma arquitetura síncrona, tudo cai; em assíncrona, a mensagem fica na fila aguardando retry. Arquitetura e Padrões Principais Produtor, Fila e Consumidor O modelo fundamental é simples: um Produtor coloca mensagens em uma Fila (ou Broker), e um ou mais Consumidores as processam. A fila garante entrega e persistência, enquanto produtor

<h2>O que é Mensageria Assíncrona e Por Que Importa</h2>

<p>Mensageria assíncrona é um padrão arquitetural onde componentes de um sistema se comunicam através de mensagens, sem esperar pela resposta imediata. Em vez de uma chamada síncrona (requisição-resposta), um serviço envia uma mensagem a uma fila e continua sua execução. Outro serviço processa essa mensagem quando estiver pronto. Isso desacopla completamente os sistemas, permitindo escalabilidade, resiliência e melhor distribuição de carga.</p>

<p>Na prática, você utiliza mensageria quando precisa processar tarefas pesadas em background, sincronizar dados entre microserviços, ou lidar com picos de tráfego sem sobrecarregar um único servidor. A diferença com chamadas síncronas é clara: se um serviço falha em uma arquitetura síncrona, tudo cai; em assíncrona, a mensagem fica na fila aguardando retry.</p>

<h2>Arquitetura e Padrões Principais</h2>

<h3>Produtor, Fila e Consumidor</h3>

<p>O modelo fundamental é simples: um <strong>Produtor</strong> coloca mensagens em uma <strong>Fila</strong> (ou Broker), e um ou mais <strong>Consumidores</strong> as processam. A fila garante entrega e persistência, enquanto produtor e consumidor não precisam conhecer um ao outro. Existem variações: point-to-point (uma mensagem para um consumidor) e publish-subscribe (uma mensagem para múltiplos consumidores).</p>

<pre><code class="language-python"># Exemplo com RabbitMQ - Produtor

import pika

connection = pika.BlockingConnection(pika.ConnectionParameters(&#039;localhost&#039;))

channel = connection.channel()

channel.queue_declare(queue=&#039;pedidos&#039;, durable=True)

mensagem = &#039;{&quot;pedido_id&quot;: 123, &quot;cliente&quot;: &quot;João&quot;, &quot;valor&quot;: 150.00}&#039;

channel.basic_publish(

exchange=&#039;&#039;,

routing_key=&#039;pedidos&#039;,

body=mensagem,

properties=pika.BasicProperties(delivery_mode=2) # mensagem persistente

)

print(&quot;Pedido enviado!&quot;)

connection.close()</code></pre>

<pre><code class="language-python"># Consumidor

import pika

import json

def processar_pedido(ch, method, properties, body):

pedido = json.loads(body)

print(f&quot;Processando pedido {pedido[&#039;pedido_id&#039;]} de {pedido[&#039;cliente&#039;]}&quot;)

Simulando processamento

salvar_no_banco(pedido)

ch.basic_ack(delivery_tag=method.delivery_tag)

connection = pika.BlockingConnection(pika.ConnectionParameters(&#039;localhost&#039;))

channel = connection.channel()

channel.queue_declare(queue=&#039;pedidos&#039;, durable=True)

channel.basic_qos(prefetch_count=1) # processa uma de cada vez

channel.basic_consume(queue=&#039;pedidos&#039;, on_message_callback=processar_pedido)

print(&#039;Aguardando mensagens...&#039;)

channel.start_consuming()</code></pre>

<h3>Dead Letter Queue e Retry</h3>

<p>Quando um consumidor falha ao processar uma mensagem, ela deve ser reprocessada. A estratégia mais comum é utilizar <strong>Dead Letter Queues (DLQ)</strong>. Se após N tentativas a mensagem continuar falhando, ela vai para uma DLQ especial onde pode ser investigada ou reprocessada manualmente.</p>

<pre><code class="language-python"># Configuração com retry automático

import pika

import json

import time

def setup_rabbitmq_with_dlq():

connection = pika.BlockingConnection(pika.ConnectionParameters(&#039;localhost&#039;))

channel = connection.channel()

Fila principal

channel.exchange_declare(exchange=&#039;pedidos_exchange&#039;, exchange_type=&#039;direct&#039;)

channel.queue_declare(

queue=&#039;pedidos_principal&#039;,

arguments={&#039;x-dead-letter-exchange&#039;: &#039;pedidos_dlx&#039;}

)

Dead Letter Exchange

channel.exchange_declare(exchange=&#039;pedidos_dlx&#039;, exchange_type=&#039;direct&#039;)

channel.queue_declare(queue=&#039;pedidos_dlq&#039;)

channel.queue_bind(exchange=&#039;pedidos_dlx&#039;, queue=&#039;pedidos_dlq&#039;, routing_key=&#039;pedidos&#039;)

return channel

def consumidor_com_retry():

channel = setup_rabbitmq_with_retry()

tentativas = 0

def callback(ch, method, properties, body):

nonlocal tentativas

try:

pedido = json.loads(body)

Simula erro aleatório

if pedido[&#039;valor&#039;] &lt; 50:

raise ValueError(&quot;Valor mínimo não atingido&quot;)

print(f&quot;Pedido processado: {pedido[&#039;pedido_id&#039;]}&quot;)

ch.basic_ack(delivery_tag=method.delivery_tag)

except Exception as e:

tentativas += 1

if tentativas &lt; 3:

ch.basic_nack(delivery_tag=method.delivery_tag, requeue=True)

time.sleep(2 ** tentativas) # backoff exponencial

else:

ch.basic_nack(delivery_tag=method.delivery_tag, requeue=False)

channel.basic_consume(queue=&#039;pedidos_principal&#039;, on_message_callback=callback)

channel.start_consuming()</code></pre>

<h2>Ferramentas Populares e Quando Usar</h2>

<h3>RabbitMQ vs Apache Kafka</h3>

<p><strong>RabbitMQ</strong> é um message broker tradicional, excelente para arquiteturas de fila. Oferece roteamento sofisticado, TTL de mensagens e DLQ nativas. Use quando você precisa garantias fortes de entrega e baixa latência.</p>

<p><strong>Apache Kafka</strong> é um event streaming platform focado em volume. Mantém histórico de mensagens, permite múltiplas leituras da mesma mensagem, e é ideal para data pipelines e event sourcing. Use quando precisa reprocessar histórico ou múltiplos consumidores independentes.</p>

<pre><code class="language-python"># Exemplo com Kafka - Produtor

from kafka import KafkaProducer

import json

producer = KafkaProducer(

bootstrap_servers=[&#039;localhost:9092&#039;],

value_serializer=lambda v: json.dumps(v).encode(&#039;utf-8&#039;)

)

evento = {&#039;tipo&#039;: &#039;pedido_criado&#039;, &#039;pedido_id&#039;: 123, &#039;timestamp&#039;: &#039;2024-01-15&#039;}

future = producer.send(&#039;eventos_pedidos&#039;, evento)

try:

record_metadata = future.get(timeout=10)

print(f&quot;Enviado para tópico {record_metadata.topic}&quot;)

except Exception as e:

print(f&quot;Erro: {e}&quot;)

producer.close()</code></pre>

<pre><code class="language-python"># Consumidor Kafka

from kafka import KafkaConsumer

import json

consumer = KafkaConsumer(

&#039;eventos_pedidos&#039;,

bootstrap_servers=[&#039;localhost:9092&#039;],

value_deserializer=lambda m: json.loads(m.decode(&#039;utf-8&#039;)),

group_id=&#039;grupo_processamento_pedidos&#039;,

auto_offset_reset=&#039;earliest&#039;

)

for message in consumer:

evento = message.value

print(f&quot;Processando evento: {evento[&#039;tipo&#039;]} - Pedido {evento[&#039;pedido_id&#039;]}&quot;)

Sua lógica aqui</code></pre>

<h2>Boas Práticas e Armadilhas Comuns</h2>

<p><strong>Idempotência</strong> é crítica: uma mensagem pode ser entregue mais de uma vez. Seu consumidor deve ser idempotente, ou seja, processar a mesma mensagem múltiplas vezes deve produzir o mesmo resultado. Use IDs únicos e verificações no banco de dados.</p>

<p><strong>Monitore suas filas</strong>: acúmulo de mensagens é sinal de que consumidores estão lentos ou falhando. Implemente alertas para profundidade de fila e tempo de processamento.</p>

<p><strong>Evite acoplamento nos schemas</strong>: versionize suas mensagens e use campos opcionais. Se um consumidor não entende um campo novo, não deve quebrar.</p>

<pre><code class="language-python"># Exemplo de processamento idempotente

import hashlib

def processar_pedido_idempotente(pedido, db):

Gera ID único baseado no conteúdo

pedido_hash = hashlib.md5(

json.dumps(pedido, sort_keys=True).encode()

).hexdigest()

Verifica se já foi processado

if db.query(&quot;SELECT * FROM pedidos WHERE hash = ?&quot;, pedido_hash):

print(&quot;Pedido já processado, ignorando duplicata&quot;)

return

Processa e registra

db.execute(&quot;INSERT INTO pedidos (hash, conteudo) VALUES (?, ?)&quot;,

pedido_hash, json.dumps(pedido))

print(f&quot;Pedido {pedido[&#039;id&#039;]} processado com sucesso&quot;)</code></pre>

<h2>Conclusão</h2>

<p>Mensageria assíncrona é fundamental em sistemas modernos. Os três pilares principais que você deve dominar são: <strong>(1) desacoplamento de componentes</strong> através de produtores e consumidores independentes, <strong>(2) confiabilidade garantida</strong> via DLQs, retries e idempotência, e <strong>(3) escolha da ferramenta correta</strong> — RabbitMQ para filas tradicionais ou Kafka para event streaming. Comece com RabbitMQ se é novo no tema; quando precisar de histórico e múltiplos consumidores, migre para Kafka.</p>

<h2>Referências</h2>

<ul>

<li><a href="https://www.rabbitmq.com/documentation.html" target="_blank" rel="noopener noreferrer">RabbitMQ Official Documentation</a></li>

<li><a href="https://kafka.apache.org/documentation/" target="_blank" rel="noopener noreferrer">Apache Kafka Documentation</a></li>

<li><a href="https://dataintensive.net/" target="_blank" rel="noopener noreferrer">Designing Data-Intensive Applications - Martin Kleppmann</a></li>

<li><a href="https://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/best-practices.html" target="_blank" rel="noopener noreferrer">AWS SQS Best Practices</a></li>

<li><a href="https://www.enterpriseintegrationpatterns.com/" target="_blank" rel="noopener noreferrer">Enterprise Integration Patterns - Hohpe &amp; Woolf</a></li>

</ul>

Comentários

Mais em Ferramentas & Produtividade

Guia Completo de Domain-Driven Design
Guia Completo de Domain-Driven Design

O que é Domain-Driven Design? Domain-Driven Design (DDD) é uma abordagem para...

O que Todo Dev Deve Saber sobre Infraestrutura como Código
O que Todo Dev Deve Saber sobre Infraestrutura como Código

O que é Infraestrutura como Código (IaC) Infraestrutura como Código é a práti...

DevOps: Do Básico ao Avançado
DevOps: Do Básico ao Avançado

O que é DevOps e Por Que Importa DevOps é uma cultura que unifica desenvolvim...