Pular para o conteúdo principal

Integração com sistema externo instável

Timeout, retry com backoff, circuit breaker e DLQ protegendo um consumer que depende de uma API externa pouco confiável.

Os estudos de caso anteriores lidaram com falhas internas (bugs, bancos de dados, índices). Este trata de um problema diferente e comum: um consumer que precisa chamar um sistema externo — fora do controle do time — que é lento ou instável, e como evitar que essa instabilidade externa derrube o processamento interno do consumer.

O cenário

O antifraude-service consome o tópico compras.autorizadas e, para cada compra, chama uma API externa de score de risco (um birô de crédito ou provedor de antifraude terceirizado) antes de decidir se aprova ou sinaliza a transação. Esse provedor externo tem instabilidade conhecida: picos de latência e indisponibilidade ocasional, fora do controle do time.

O problema sem proteção

Sem timeout e circuit breaker, uma falha externa vira um problema interno

Sem um timeout configurado, uma chamada lenta ao provedor externo prende a thread do consumer indefinidamente — e, sem circuit breaker, cada mensagem processada durante uma instabilidade do provedor tenta a mesma chamada fadada a ser lenta, multiplicando o problema em vez de isolá-lo. O resultado, sem essas proteções: o antifraude-service acumula consumer lag rapidamente, e o efeito se propaga como se fosse um problema do próprio Kafka ou do próprio consumer — quando a causa raiz é inteiramente externa.

Timeout: o primeiro nível de proteção

Toda chamada ao provedor externo tem um timeout curto e explícito (por exemplo, 2 segundos) — se o provedor não responder nesse intervalo, a chamada é tratada como falha, liberando a thread do consumer para seguir processando (ou tentar novamente, dependendo da estratégia de retry).

Circuit Breaker: o segundo nível

Closedchamadas normaisOpenrejeita imediatamenteHalf-Opentesta 1 chamadamuitas falhasapós timeoutchamada de teste teve sucessochamada de teste falhou
Closed permite chamadas normalmente; após muitas falhas seguidas, o circuito abre e rejeita chamadas imediatamente sem nem tentar; após um tempo, testa uma chamada em half-open.

Um circuit breaker evita que o consumer continue tentando chamar um provedor que já demonstrou estar indisponível: depois de um número de falhas consecutivas, o circuito abre e passa a rejeitar chamadas imediatamente, sem sequer tentar a chamada de rede — economizando tempo e evitando amplificar a instabilidade do provedor externo com mais tráfego. Depois de um intervalo configurado, o circuito entra em half-open e testa uma única chamada: se ela suceder, o circuito fecha e volta ao normal; se falhar, ele abre de novo.

Dica de entrevista

Circuit breaker e retry resolvem problemas diferentes e complementares: retry lida com uma falha pontual e transitória (uma requisição específica falhou); circuit breaker lida com uma instabilidade sustentada (o provedor está fora do ar há um tempo), evitando que o consumer insista em chamadas fadadas ao fracasso. Mencionar essa diferença demonstra entendimento além do padrão isolado.

Retry, backoff e DLQ

Quando o circuito está fechado mas uma chamada falha pontualmente (timeout ou erro 5xx do provedor), o consumer trata isso como erro transitório (Capítulo 9): retry com backoff exponencial, algumas tentativas. Se o circuito estiver aberto (rejeitando de cara) ou as tentativas se esgotarem, a mensagem vai para uma DLQ específica — compras.autorizadas.antifraude.dlq — para reprocessamento manual ou automático assim que o provedor externo se recuperar.

Reprocessamento manual

"As mensagens na DLQ por instabilidade externa devem ser descartadas"

Diferente de uma DLQ por erro permanente (payload malformado), mensagens na DLQ por instabilidade de um provedor externo geralmente devem ser reprocessadas assim que o provedor se recupera — o erro não era do dado, era da disponibilidade momentânea de um sistema terceiro. Um processo (manual ou automatizado) monitora a saúde do provedor e, quando ele volta a responder normalmente, republica as mensagens da DLQ de volta ao fluxo principal.

Idempotência continua necessária

Mesmo com todas essas proteções, o consumer precisa continuar idempotente (Capítulo 11): se uma chamada ao provedor externo suceder, mas o consumer cair antes de comitar o offset, a mensagem será reprocessada — e a chamada ao provedor externo (que pode ter custo financeiro por chamada, no caso de birôs de crédito) não deveria ser refeita desnecessariamente. Uma tabela de cache de resultado por eventId evita tanto duplicidade de efeito quanto custo desnecessário de chamadas repetidas ao provedor.

Observabilidade

Sinais que distinguem problema interno de problema externo

SinalAponta para
Consumer lag alto + circuit breaker abertoProblema é do provedor externo, não do consumer
Consumer lag alto + circuit breaker fechado + baixa taxa de erroProblema é de capacidade de processamento interno
Taxa de mensagens na DLQ correlacionada com indisponibilidade conhecida do provedorConfirma causa externa, direciona a resposta para reprocessamento em vez de investigação de código

Resumo

Consumers que dependem de sistemas externos instáveis precisam de camadas de proteção específicas: timeout para não travar threads indefinidamente, circuit breaker para parar de insistir em um provedor já identificado como fora do ar, e retry com backoff para as falhas pontuais que ainda vale a pena tentar de novo. DLQ isola o que não foi possível processar durante a instabilidade, para reprocessamento assim que o provedor externo se recupera — e idempotência continua obrigatória, tanto por correção quanto, neste caso, por custo.

Capítulos relacionados