Sistemas orientados a eventos processam trabalho de forma assíncrona através de mensagens. Isso muda fundamentalmente como medimos e otimizamos performance. Métricas tradicionais como "tempo de resposta" precisam ser repensadas.
Em sistemas síncronos, você mede latência. Em sistemas assíncronos, você mede throughput e lag.
Características de Sistemas Event-Driven
Fluxo síncrono vs assíncrono
Síncrono:
Request → Processamento → Response
[100ms total]
Assíncrono:
Request → Queue → ACK (5ms)
↓
Consumer processa (100ms)
↓
Resultado disponível
Métricas diferentes
| Síncrono | Assíncrono |
|---|---|
| Latência end-to-end | End-to-end latency |
| Tempo de resposta | Consumer lag |
| Requests/segundo | Messages/segundo |
| Timeout | TTL (Time to Live) |
Métricas Essenciais
1. Consumer Lag
Diferença entre última mensagem publicada e última consumida.
Produtor publicou: mensagem #1000
Consumer processou: mensagem #800
Consumer lag: 200 mensagens
Em tempo:
Lag em mensagens: 200
Throughput do consumer: 50 msg/s
Lag em tempo: 200/50 = 4 segundos
Prometheus + Kafka:
# Lag por consumer group
kafka_consumergroup_lag{topic="orders"}
2. Throughput
Producer throughput: 1000 msg/s
Consumer throughput: 800 msg/s
Gap: 200 msg/s
→ Lag aumentará 200 msg/s continuamente
→ Sistema não é sustentável
Regra de ouro:
Consumer throughput ≥ Producer throughput
(com margem para picos)
3. Processing Time
Tempo para processar uma mensagem individual.
Mensagem recebida: 14:00:00.000
Processamento iniciou: 14:00:00.005
Processamento terminou: 14:00:00.105
Processing time: 100ms
Wait time: 5ms
4. End-to-End Latency
Tempo total desde produção até processamento completo.
Evento criado: 14:00:00.000
Publicado no broker: 14:00:00.010
Consumido: 14:00:00.050
Processado: 14:00:00.150
End-to-end: 150ms
Gargalos Comuns
1. Consumer lento
Producer: 1000 msg/s
Consumer: 100 msg/s
Lag após 1 hora: 3.24 milhões de mensagens
Soluções:
1. Mais consumers (partições paralelas)
2. Batching no processamento
3. Otimizar lógica do consumer
4. Async I/O dentro do consumer
2. Partições desbalanceadas
Partição 0: 500 msg/s → Consumer A (sobrecarregado)
Partição 1: 100 msg/s → Consumer B (ocioso)
Partição 2: 100 msg/s → Consumer C (ocioso)
Soluções:
1. Melhorar partition key
2. Mais partições
3. Rebalanceamento manual
3. Broker saturado
Broker CPU: 95%
Disco: 90% IOPS
Network: saturada
→ Latência de publicação aumenta
→ Consumers recebem atrasado
Soluções:
1. Mais brokers
2. Compression
3. Batching de producers
4. Retention menor
4. Reprocessamento infinito
Mensagem falha → Volta para fila
Falha novamente → Volta para fila
...infinitamente
→ Recursos consumidos por mensagens que nunca vão passar
Soluções:
1. Dead Letter Queue (DLQ)
2. Retry com limite
3. Exponential backoff
# Retry com DLQ
max_retries = 3
retry_count = message.headers.get('retry_count', 0)
if retry_count >= max_retries:
send_to_dlq(message)
else:
try:
process(message)
except Exception:
message.headers['retry_count'] = retry_count + 1
republish_with_delay(message)
Padrões de Otimização
1. Batching
# ❌ Commit por mensagem
for message in messages:
process(message)
consumer.commit() # I/O por mensagem
# ✅ Batch commit
batch = []
for message in messages:
batch.append(process(message))
if len(batch) >= 100:
save_batch(batch)
consumer.commit()
batch = []
2. Prefetch
# Kafka consumer config
fetch.min.bytes = 1024 # Espera acumular dados
fetch.max.wait.ms = 500 # Ou até 500ms
max.poll.records = 500 # Até 500 registros por poll
3. Paralelismo
# Consumer com thread pool
from concurrent.futures import ThreadPoolExecutor
executor = ThreadPoolExecutor(max_workers=10)
for message in consumer:
executor.submit(process, message)
Cuidado: Ordem não garantida com paralelismo!
4. Compression
# Producer config
compression.type = 'lz4' # Rápido
# ou 'zstd' # Melhor compressão
Sem compressão: 100MB/s bandwidth
Com LZ4: 25MB/s bandwidth (4x redução)
Testando Sistemas Event-Driven
Desafios
Síncrono:
- Request → Response
- Tempo medido diretamente
Assíncrono:
- Publish → ??? → Resultado em algum lugar
- Como medir end-to-end?
Técnicas
1. Correlation IDs
# Producer
event = {
'correlation_id': uuid4(),
'timestamp': time.time(),
'data': {...}
}
publish(event)
store_correlation(event['correlation_id'], event['timestamp'])
# Consumer
def process(event):
result = do_work(event)
publish_result({
'correlation_id': event['correlation_id'],
'completed_at': time.time()
})
# Collector
def collect_result(result):
start = get_stored_timestamp(result['correlation_id'])
end_to_end = result['completed_at'] - start
record_metric('e2e_latency', end_to_end)
2. Synthetic events
# Injeta eventos de teste periodicamente
@every(minute=1)
def inject_synthetic():
publish({
'type': 'synthetic_test',
'timestamp': time.time()
})
# Consumer detecta e mede
def process(event):
if event['type'] == 'synthetic_test':
latency = time.time() - event['timestamp']
record_metric('synthetic_e2e', latency)
return # Não processa
# Processamento normal
3. Load testing assíncrono
// k6 para sistemas assíncronos
import { check } from 'k6';
import kafka from 'k6/x/kafka';
export default function() {
// Publica
kafka.produce({
topic: 'orders',
messages: [{ value: JSON.stringify({order_id: __VU}) }]
});
// Verifica resultado (eventual)
// Polling em outro tópico ou banco
}
Métricas de teste
1. Throughput máximo sustentável
- Aumenta carga até lag começar a crescer
- Ponto antes = throughput máximo
2. Latência sob carga
- Mede end-to-end em diferentes níveis de carga
- Identifica ponto de inflexão
3. Recovery time
- Injeta pico, para
- Mede tempo até lag zerar
Monitoramento em Produção
Dashboard essencial
1. Consumer Lag (absoluto e em tempo)
2. Throughput: produced vs consumed
3. Processing time (p50, p95, p99)
4. Error rate por consumer
5. Partition distribution
6. Broker health
Alertas
# Consumer lag crescendo
- alert: ConsumerLagIncreasing
expr: |
delta(kafka_consumergroup_lag[5m]) > 1000
for: 5m
# Lag absoluto alto
- alert: ConsumerLagHigh
expr: |
kafka_consumergroup_lag > 10000
for: 2m
# Consumer parado
- alert: ConsumerStalled
expr: |
rate(kafka_consumer_records_consumed_total[5m]) == 0
for: 5m
Conclusão
Performance em sistemas event-driven requer mindset diferente:
- Foco em throughput, não só latência
- Consumer lag é a métrica mais importante
- Balanceamento de partições é crítico
- Retry e DLQ previnem cascatas de falha
- Testes end-to-end requerem correlation IDs
A regra fundamental:
Consumer throughput > Producer throughput × margem de segurança
Se essa equação não fecha, lag só vai crescer.
Sistema event-driven saudável tem lag próximo de zero. Lag crescente é dívida técnica acumulando juros.