Retry, Backoff e Circuit Breaker
Três tentativas antes da DLQ
Encadeando retry com envio automático para payment.dlq após falhas persistentes.
Nesta aula você vai
- Aplicar limite de 3 tentativas antes da DLQ
- Transportar metadados de tentativa em headers
- Garantir fluxo auditável no payment-service
Três tentativas antes da DLQ
Objetivos
- Garantir retry controlado antes de descartar para DLQ.
- Padronizar metadados de tentativas.
- Reduzir impacto de falhas persistentes no throughput.
Pré-requisitos
- Aula de retry exponencial concluída.
- Publisher de DLQ implementado na matéria 9.
- Consumer Kafka em execução no
payment-service.
Conceito
Política alvo: tentar até 3 vezes em erro transitório. Se todas falharem, enviar para payment.dlq com headers de rastreio. Isso evita retries infinitos e facilita operação.
Estrutura de arquivos
services/payment-service/internal/
├── consumer/payment_consumer.go
├── retry/runner.go
└── dlq/publisher.go
Passo a passo
- Fluxo no consumer:
func (c *Consumer) processWithRetry(ctx context.Context, msg kafka.Message, event PaymentRequested) error {
attempts := 0
err := retry.Run(ctx, c.retryPolicy, func() error {
attempts++
c.logger.Info("tentativa de autorização", "attempt", attempts, "orderId", event.OrderID)
return c.gateway.Authorize(ctx, event.OrderID, event.AmountCents)
}, errors.IsRetryable)
if err == nil {
return nil
}
if errors.IsRetryable(err) {
return c.dlqPublisher.Publish(ctx, msg, err, attempts)
}
return c.dlqPublisher.Publish(ctx, msg, err, attempts)
}
- Propague header de tentativas:
func appendAttemptHeader(msg kafka.Message, attempts int) kafka.Message {
msg.Headers = append(msg.Headers, kafka.Header{
Key: "x-retry-attempts",
Value: []byte(strconv.Itoa(attempts)),
})
return msg
}
- Ajuste publisher DLQ para ler tentativas finais:
func (p *Publisher) Publish(ctx context.Context, original kafka.Message, reason error, attempts int) error {
original = appendAttemptHeader(original, attempts)
// ... envia para payment.dlq
return p.writer.WriteMessages(ctx, original)
}
Como testar
Simule gateway indisponível:
docker compose up -d kafka payment-service
echo '{"type":"payment.requested","payload":{"orderId":"o-99","amountCents":4500}}' \
| kcat -P -b localhost:9092 -t payment.events
docker compose logs -f payment-service
kcat -C -b localhost:9092 -t payment.dlq -o end -e -f 'Headers=%h Value=%s\n'
Valide x-retry-attempts=3 no evento enviado para DLQ.
Dicas de projeto
- Fixe política de retry em configuração central.
- Evite variação de tentativas por serviço sem justificativa.
- Logue tentativa e tempo de espera entre elas.
Erros comuns
- Enviar para DLQ antes de completar 3 tentativas.
- Não incluir metadado de tentativas no evento.
- Tratar erro transitório e erro de payload do mesmo jeito.
Resumo
Você encadeou retry exponencial com política de três tentativas antes da DLQ, com rastreabilidade completa por header.