Circuito Breaker e Retrai
Circuito Breaker e Retrai
1) Por que é necessário
As redes não são confiáveis, a latência pulsa, os nós caem e os limites são alcançados. Os retais são salvos de falhas de curto prazo, enquanto o Circuito Breaker protege o sistema contra falhas em cascata e «se-DDoS». A combinação com os horários e limites corretos mantém o SLO, estabiliza os atrasos de cauda e o preço das donzelas.
2) Princípios básicos
Primeiro os temporizadores, depois os retais, depois o Circuito Breaker.
Retraim apenas operações idumpotentes (GET, POST seguro/PUT com chave idumpotente).
Desembolsa o orçamento de retrações: ≤ 10% a 15% do RPS original para a rota.
Localiza a rejeição: bolkhead (pulas/quotas separadas) + rate-limit.
Em caso de degradação, falha rápida (fail-fast), graceful-degradation/broncas.
3) Semântica de retrações
Quando retrabalhar
Erros de transito timeouts, 5xx, indisponibilidade de rede, 429 (após 'Retry-After').
Não é possível retrabalhar: erros de negócio explícitos (4xx ≠ 429), side-effects sem idempotação (pagamento sem chave).
Estratégias
Exponential backoff + jitter (total ou uniforme): suaviza as manadas de retrações.
Max attempts: 1-2 (raramente 3) - mais prejudicial.
Budget: Contador global de retrações/segundos por serviço e per-request «retry tokens».
Hedging (raramente): tiragem paralela após t-quantili (p95) - somente para leitura rigorosamente idempotante.
python base = 100 # ms for attempt in range(1, max_attempts+1):
try:
return call()
except Transient as e:
if attempt == max_attempts: raise sleep_ms = min(cap_ms, base 2(attempt-1))
sleep(random(0, sleep_ms)) # full jitter
4) Timeouts e «falha rápida»
Cliente timeout <upstream timeout: para não poupar solicitações «zumbi».
Делите: connect timeout, read timeout, overall deadline.
Tail-aware temporizadores: apontando para p95/p99 + pequeno estoque.
Use o campo de deadline compartilhado (por exemplo, gRPC 'deadline') e espalhe-o para baixo na cadeia.
5) Circuito Breaker: como funciona
Estados:- Closed: omite o tráfego, conta erros/latência.
- Open: é imediatamente recusado (ou uma resposta de reposição).
- Half-Open: solicitações de verificação; com sucesso, fecha-se.
- Erros/temporizações superam a proporção X% por janela N/segundos ou p99 acima do limite.
- Estatísticas deslizantes relevantes e volume mínimo (por exemplo, ≥ 50 consultas).
6) Bolkhead, quotas e «divida-te»
Balas separadas de conexões per-upstream e per-fic.
Quotas de in-flight consultas; a mais é uma rejeição rápida.
Quando há escassez, a degradação do fichá de baixa prioridade (função flags).
7) Integração com o perímetro (Envoy/Istio/Nginx)
Envoy (retry + outlier + CB, ideia):yaml routes:
- match: { prefix: "/api" }
route:
cluster: upstream_api timeout: 2s retry_policy:
retry_on: "connect-failure,reset,retriable-4xx,5xx"
num_retries: 2 per_try_timeout: 600ms retry_back_off: { base_interval: 100ms, max_interval: 800ms }
hedge_policy:
hedge_on_per_try_timeout: true initial_requests: 1 additional_request_chance: { numerator: 5, denominator: HUNDRED } # 5%
clusters:
- name: upstream_api circuit_breakers:
thresholds:
- priority: DEFAULT max_connections: 500 max_requests: 1000 max_retries: 200 outlier_detection:
consecutive_5xx: 5 interval: 5s base_ejection_time: 30s max_ejection_percent: 50
Istio (VirtualService fault/retry, exemplo comprimido):
yaml apiVersion: networking. istio. io/v1beta1 kind: VirtualService spec:
hosts: ["payments"]
http:
- route: [{ destination: { host: payments } }]
timeout: 2s retries:
attempts: 2 perTryTimeout: 600ms retryOn: "5xx,connect-failure,refused-stream,reset"
Nginx Ingress (anotações):
yaml nginx. ingress. kubernetes. io/proxy-connect-timeout: "2"
nginx. ingress. kubernetes. io/proxy-read-timeout: "2"
nginx. ingress. kubernetes. io/proxy-next-upstream: "error timeout http_502 http_503 http_504"
nginx. ingress. kubernetes. io/proxy-next-upstream-tries: "2"
8) Bibliotecas e código (pilhas)
Java (Resilience4j):java var cb = CircuitBreaker. ofDefaults("psp");
var retry = Retry. of("psp-retry",
RetryConfig. custom()
.maxAttempts(2)
.waitDuration(Duration. ofMillis(200))
.intervalFunction(IntervalFunction. ofExponentialRandomBackoff(100, 2. 0, 0. 5) )//jitter
.retryExceptions(SocketTimeoutException. class, IOException. class)
.build());
Supplier<Response> decorated =
CircuitBreaker. decorateSupplier(cb,
Retry. decorateSupplier(retry, () -> client. call()));
return Try. ofSupplier(decorated)
.recover(BusinessException. class, fallback())
.get();
Go (context deadline + backoff):
go ctx, cancel:= context. WithTimeout(context. Background(), 2time. Second)
defer cancel()
var lastErr error for i:= 0; i < 2; i++ {
reqCtx, stop:= context. WithTimeout(ctx, 600time. Millisecond)
lastErr = call(reqCtx)
stop()
if lastErr == nil { break }
sleep:= time. Duration(rand. Intn(1<<uint(7+i))) time. Millisecond // full jitter time. Sleep(min(sleep, 800time. Millisecond))
}
if lastErr!= nil { return fastFail() }
Node. js (got + p-retry):
js import pRetry from 'p-retry';
await pRetry(() => got(url, { timeout: { connect: 500, request: 2000 } }), {
retries: 2,
factor: 2,
randomize: true,
minTimeout: 100,
maxTimeout: 800,
onFailedAttempt: e => { if (isBusiness(e)) throw e; }
});
9) Orçamento de retrações e SLO
Digite retry tokens: cada retrai gasta token; o pool está limitado.
Vincule-se ao error-boodget: quando o burn-rate estiver acima do limite - desligando os retais, abrindo o CB com mais frequência, ativando a degradação.
Lançamentos canários: em canários, reduza as tentativas e os tokens.
10) Hedging (cuidado)
Inicie um pedido adicional depois do p95-deadline, cancelando o perdedor.
Somente para leituras e operações idumpotentes «seguras»; limite a participação (≤ 1% a 5%).
Acompanhe a carga de trabalho do upstream.
11) Observabilidade
As métricas RED são Rate, Erro, Duration (p50/p95/p99).
Métricas CB: estado (open/half-open), taxa de abertura, pedidos omitidos/reprovados.
Retrai: attempts/request, retry-rate, tokens queimados.
Perímetro: outler-ejation, ejation-rate.
Trailers: anote 'retry _ attempt',' cb _ state ',' hedged = true ', e perca' trace _ id '.
12) Integração com a arquitetura
Bolkhead + CB para cada upstream crítico.
Filas/asinharão, para longas operações em vez de temporizações loucas.
Kesh/broncas: para fies não-críticos em fail-open.
O scale automático não compensa os maus retais. Primeiro, pare a tempestade.
13) Anti-pattern
Retraias sem temporizadores → conectórios «dependentes» e a exaustão das balas.
Repetição de Operações Não-Identificadas (Duplo Débito).
Crescimento exponencial infinito sem cap ou jitter.
Um CB único para todos os upstream → arrastar a falha para todo o produto.
Ignorar 429/' Retry-After '.
O tempo de espera do cliente é mais longo do que o upstream (ou não).
«Tratar» erros de negócios com retais.
14) Folha de cheque de implementação (0-30 dias)
0-7 dias
Identifique as rotas e a sua idempotidade.
Defina os temporizadores (connect/read/overall) e inclua os retais mínimos (x 1) e CB padrão.
Divida os pools/quotas (bulkhead) para upstream básicos.
8 a 20 dias
Inclua o jitter e o orçamento global de retrações, alertas por retry-rate.
Configure o outler-ejation no perímetro, uma falha rápida para o fic low-prio.
Dashboards RED + CB/Retry, trailers com marcas.
21-30 dias
Perfis de canário retrações (menos tentativas), game-day «upstream lento/flaped».
Documente a política: quem/o que retraita, limites, exceções.
Reveja o p95/p99 e os temporizadores de dados, não de olho.
15) Métricas de maturidade
100% das rotas têm timeouts e uma política documentada de retrações/ST.
O Retry-rate está no orçamento (≤ 10% a 15%) e não há picos nos incidentes.
Os CB são executados antes que todo o pulo caia; Não há falhas em cascata.
Trailers mostram tentativas/hedging; p99 está estável em picos.
Os lançamentos canários usam um perfil de retais «poupado».
16) Exemplos curtos de configuração
Resilience4j YAML (Spring Boot, идея):yaml resilience4j:
circuitbreaker:
instances:
psp:
slidingWindowType: COUNT_BASED slidingWindowSize: 100 minimumNumberOfCalls: 50 failureRateThreshold: 50 waitDurationInOpenState: 30s permittedNumberOfCallsInHalfOpenState: 5 retry:
instances:
psp:
maxAttempts: 2 waitDuration: 200ms enableExponentialBackoff: true exponentialBackoffMultiplier: 2. 0 retryExceptions:
- java. net. SocketTimeoutException
- java. io. IOException
Envoy rate-limit (pedaço de ideia):
yaml rate_limits:
- actions:
- generic_key: { descriptor_value: "api. payments" }
17) Conclusão
Sustentabilidade é disciplina: timeouts → retais (com jitter e orçamento) → circuito Breaker + bolkhead/quotas e renúncia rápida. Configure o perímetro (outlier-ejation), pendure os dashboards RED/CB/Retry, fixe a política de idempotação e não se esqueça do SLI de negócios. Assim, as breves falhas permanecerão imperceptíveis, e os verdadeiros incidentes não se transformarão em quedas em cascata.