Resiliência: disjuntores e novas tentativas
Mantenha os sistemas saudáveis durante falhas parciais.
Resiliência: disjuntores e novas tentativas é uma aula grátis de PHP Academy no CoddyKit. Esta é a aula 4 de 4. Você pode ler a aula completa abaixo gratuitamente — depois pratica ao vivo no navegador com um editor de código integrado e um tutor de IA 24/7. Faz parte do caminho de aprendizado de PHP Academy, e seu progresso é sincronizado entre a web e o app CoddyKit. O curso de PHP Academy inclui 4 aulas no total.
Padrões de resiliência
Em um sistema distribuído, a falha parcial é o estado normal — alguma dependência está sempre lenta, reiniciando ou sobrecarregada. Resiliência consiste em projetar cada serviço para que uma dependência com problemas não derrube o seu serviço e, depois, todo o sistema.
Esta lição aborda o conjunto de ferramentas essenciais: tempos limite, novas tentativas com atraso, disjuntores, compartimentação e degradação controlada — tudo em PHP.
Primeiro, os tempos limite
A configuração de resiliência mais importante é o tempo limite. Sem ele, um serviço de destino lento mantém seus trabalhadores do PHP-FPM esperando; as requisições se acumulam; você fica sem trabalhadores; e seu serviço cai porque outra pessoa estava lenta. Essa é a clássica falha em cascata.
Defina um tempo limite de conexão e um tempo limite total de requisição em todas as chamadas externas. Sempre.
<?php
require 'vendor/autoload.php';
use GuzzleHttp\Client;
$http = new Client([
'connect_timeout' => 0.5, // fail fast if we can't even connect
'timeout' => 2.0, // hard cap on the whole call
]);
// A hung dependency now fails in 2s instead of holding a worker forever.Novas tentativas — com cuidado
Falhas transitórias (uma instabilidade breve, uma curta interrupção de rede, um 503) geralmente têm sucesso em uma segunda tentativa. Mas novas tentativas são perigosas: faça tentativas com excesso de rapidez e você amplificará a carga sobre um serviço que já está enfrentando dificuldades. Regras:
- Faça novas tentativas apenas para operações idempotentes e erros que permitem nova tentativa (tempos limite, 5xx, 429).
- Limite o número de tentativas.
- Nunca faça novas tentativas para erros de cliente 4xx — eles simplesmente falharão novamente.
Atraso exponencial + aleatoriedade
Novas tentativas em intervalos fixos feitas por muitos clientes se sincronizam, formando uma avalanche de solicitações que martela o serviço em recuperação em várias ondas. A solução é usar atraso exponencial (dobrar o atraso a cada tentativa) mais aleatoriedade para distribuir a carga.
<?php
function backoffDelay(int $attempt, float $base = 0.1, float $cap = 5.0): float {
$exp = min($cap, $base * (2 ** $attempt)); // 0.1, 0.2, 0.4, ...
return mt_rand(0, (int)($exp * 1000)) / 1000; // full jitter: 0..exp
}
for ($i = 0; $i < 5; $i++) {
printf("attempt %d -> wait %.3fs\n", $i, backoffDelay($i));
}Um ciclo de tentativas
Combinando as peças: limite o número de tentativas, faça novas tentativas apenas para erros que permitem nova tentativa, aguarde entre as tentativas usando um atraso com aleatoriedade e propague a última falha se todas as tentativas falharem.
<?php
function withRetry(callable $op, int $maxAttempts = 4): mixed {
$attempt = 0;
while (true) {
try {
return $op();
} catch (\Throwable $e) {
$attempt++;
if ($attempt >= $maxAttempts || !isRetryable($e)) {
throw $e; // give up
}
usleep((int)(backoffDelay($attempt) * 1_000_000));
}
}
}
function isRetryable(\Throwable $e): bool { return $e->getCode() === 0 || $e->getCode() >= 500; }
function backoffDelay(int $a): float { return min(5.0, 0.1 * (2 ** $a)) * (mt_rand(0, 100) / 100); }O disjuntor
Novas tentativas ajudam com instabilidades breves, mas, se uma dependência estiver realmente indisponível, tentar novamente cada requisição apenas desperdiça tempo e recursos. Um disjuntor monitora as falhas e, quando elas ultrapassam um limite, abre — interrompendo as chamadas e falhando instantaneamente, em vez de esperar por um serviço indisponível.
Ele tem três estados: Fechado (as chamadas fluem e as falhas são contabilizadas), Aberto (as chamadas são rejeitadas imediatamente) e Semiaberto (algumas chamadas de teste verificam a recuperação).
Máquina de estados do disjuntor
As transições são estas: Fechado → Aberto quando as falhas ultrapassam o limite. Aberto → Semiaberto após um período de resfriamento. Semiaberto → Fechado após uma verificação bem-sucedida, ou de volta a Aberto em caso de falha. O estado precisa ser compartilhado entre os processos PHP (Redis/APCu), pois cada requisição é um processo novo.
<?php
final class CircuitBreaker {
public function __construct(
private int $threshold = 5,
private int $coolDown = 30, // seconds
) {}
public function call(callable $op, array &$state): mixed {
if ($state['status'] === 'open') {
if (time() - $state['openedAt'] < $this->coolDown) {
throw new \RuntimeException('Circuit open - failing fast');
}
$state['status'] = 'half-open'; // time to probe
}
try {
$result = $op();
$state = ['status' => 'closed', 'failures' => 0]; // recovered
return $result;
} catch (\Throwable $e) {
$state['failures'] = ($state['failures'] ?? 0) + 1;
if ($state['failures'] >= $this->threshold || $state['status'] === 'half-open') {
$state['status'] = 'open';
$state['openedAt'] = time();
}
throw $e;
}
}
}Compartimentação
Com nome inspirado nos compartimentos de navios, o padrão de compartimentação isola recursos para que uma falha em uma área não afunde toda a embarcação. Se todos os seus trabalhadores puderem acessar o serviço Reports lento, uma indisponibilidade do Reports poderá consumir todos os trabalhadores e deixar o Checkout sem recursos.
Particione os recursos — conjuntos de conexões separados, conjuntos de trabalhadores/filas separados por dependência ou limites de concorrência por serviço de destino — para que uma dependência com falha esgote apenas sua própria parcela.
Degradação controlada e alternativas
Quando uma dependência não crítica estiver indisponível, degrade o serviço em vez de gerar um erro. Exiba um valor armazenado em cache, um valor padrão ou uma experiência reduzida. O estado aberto do disjuntor é o gatilho natural para o caminho alternativo.
<?php
function getRecommendations(callable $remoteCall, Redis $cache, string $userId): array {
try {
$recs = $remoteCall($userId);
$cache->setex("recs:$userId", 3600, json_encode($recs));
return $recs;
} catch (\Throwable $e) {
// Fallback 1: last-known-good from cache
if ($cached = $cache->get("recs:$userId")) {
return json_decode($cached, true);
}
// Fallback 2: generic popular items - never block the page
return ['popular-1', 'popular-2'];
}
}Chaves de idempotência para novas tentativas seguras
Novas tentativas só são seguras em operações idempotentes. Para uma ação não idempotente, como "cobrar o cartão", anexe uma chave de idempotência que o servidor use para eliminar duplicatas: se uma nova tentativa chegar depois que a primeira já tiver sido bem-sucedida (mas a resposta tiver sido perdida), o servidor retornará o resultado original em vez de cobrar duas vezes.
<?php
function chargeWithRetry(callable $http, string $orderId, int $cents): array {
// Same key across all retries of THIS logical charge
$key = 'charge-' . $orderId;
return withRetry(fn() => $http('POST', '/charges', [
'headers' => ['Idempotency-Key' => $key],
'json' => ['order' => $orderId, 'amount' => $cents],
]));
}
function withRetry(callable $op) { return $op(); } // see earlier sceneJuntando tudo
Os padrões se compõem, e a ordem importa. Uma chamada externa robusta normalmente é aninhada assim:
- Tempo limite em cada tentativa individual (mais interna).
- Nova tentativa com atraso envolvendo a chamada com tempo limite para falhas transitórias.
- Disjuntor envolvendo a nova tentativa, para que uma indisponibilidade prolongada o acione rapidamente.
- Compartimentação limitando a capacidade que essa dependência pode consumir.
- Alternativa na camada mais externa, capturando tudo o que for propagado.
Existem bibliotecas com invólucros no estilo Resilience4PHP, mas entender as camadas é mais importante do que qualquer pacote específico.
Verificação rápida
Escolher o padrão correto.
Recapitulação
Sobrevivendo a falhas parciais:
- Tempos limite em todas as chamadas evitam o esgotamento dos trabalhadores e as falhas em cascata.
- Novas tentativas apenas para operações idempotentes e erros que permitem nova tentativa, com atraso exponencial + aleatoriedade.
- Disjuntores falham rapidamente (Fechado → Aberto → Semiaberto) quando uma dependência está indisponível.
- Compartimentação isola recursos para que uma falha não deixe tudo sem recursos.
- Degradação controlada / alternativas mantêm os fluxos essenciais ativos.
Juntos, esses padrões transformam falhas inevitáveis em eventos contidos e recuperáveis.
Aprenda PHP com um tutor de IA — grátis
Escreva e execute código real no seu navegador, obtenha ajuda instantânea de um tutor de IA 24/7 e continue de onde parou na web ou no app.
- Cursos
- 49
- Aulas
- 195
Perguntas Frequentes
A aula “Resiliência: disjuntores e novas tentativas” é grátis?
Sim — o texto completo de “Resiliência: disjuntores e novas tentativas” é grátis para ler aqui na web. Para praticá-la interativamente (um editor de código integrado e um tutor de IA 24/7) e desbloquear o restante do curso de PHP Academy, atualize para CoddyKit PRO. O curso de PHP Academy inclui 4 aulas no total.
O que vou aprender em “Resiliência: disjuntores e novas tentativas”?
Mantenha os sistemas saudáveis durante falhas parciais. Você pratica PHP Academy com código prático que executa diretamente no navegador, e um tutor de IA 24/7 responde suas dúvidas enquanto trabalha na aula.
Preciso ter experiência prévia para começar PHP Academy?
Nenhuma experiência prévia é necessária. PHP Academy no CoddyKit é estruturado para alunos iniciantes até avançados, então você pode começar aqui ou desde o início e aprender no seu ritmo. Esta é a aula 4 de 4.
Quanto tempo leva a aula “Resiliência: disjuntores e novas tentativas”?
A maioria das aulas CoddyKit leva cerca de 5–10 minutos. Cada uma é compacta e interativa, então você faz progresso constante e retoma exatamente de onde parou entre web e app.
Posso escrever e executar código nesta aula de PHP Academy?
Sim. Cada aula de PHP Academy inclui um editor de código integrado, então você escreve e executa código real direto no navegador e recebe feedback de IA instantaneamente — nenhuma configuração local necessária.
Todas as aulas deste curso
- Do monólito aos microsserviços
- Comunicação entre serviços: REST e gRPC
- Gateways de API e descoberta de serviços
- Resiliência: disjuntores e novas tentativas