PHP: Implementación de Estrategias de Reintento Inteligente con Circuit Breakers
En el desarrollo de aplicaciones robustas y tolerantes a fallos, es crucial implementar estrategias de reintento para manejar errores transitorios. Sin embargo, un reintento ingenuo puede empeorar la situación si el servicio subyacente está continuamente fallando. Para mitigar este problema, podemos implementar un patrón Circuit Breaker junto con reintentos inteligentes. Esta técnica detiene los reintentos después de un cierto número de fallos, protegiendo así los recursos y evitando la sobrecarga del sistema.
<?php
class CircuitBreaker {
private $failures = 0;
private $threshold;
private $retryTimeout;
private $state = 'CLOSED'; // OPEN, HALF_OPEN, CLOSED
private $lastFailureTime;
public function __construct(int $threshold = 5, int $retryTimeout = 60) {
$this->threshold = $threshold;
$this->retryTimeout = $retryTimeout;
}
public function call(callable $operation) {
if ($this->state === 'OPEN' && time() - $this->lastFailureTime < $this->retryTimeout) {
throw new \Exception("Circuit Breaker is OPEN. Retrying after {$this->retryTimeout} seconds.");
}
try {
$result = $operation(); // Ejecutar la operación propensa a fallar
$this->reset(); // Operación exitosa, cerrar el circuito
return $result;
} catch (\Exception $e) {
$this->recordFailure(); // Registrar el fallo
throw $e; // Re-lanzar la excepción para manejo externo
}
}
private function recordFailure() {
$this->failures++;
$this->lastFailureTime = time();
if ($this->failures >= $this->threshold) {
$this->open();
}
}
private function open() {
$this->state = 'OPEN';
echo "Circuit Breaker OPENED.\n";
}
private function reset() {
$this->failures = 0;
$this->state = 'CLOSED';
echo "Circuit Breaker CLOSED.\n";
}
}
Este código define una clase CircuitBreaker
que envuelve la lógica susceptible a errores. El constructor recibe un umbral de fallos (threshold
) y un tiempo de espera para el reintento (retryTimeout
). El método call
ejecuta la operación proporcionada. Si la operación falla, recordFailure
incrementa el contador de fallos. Si el número de fallos excede el umbral, el circuito se abre (OPEN
). Mientras el circuito está abierto, las llamadas sucesivas lanzan una excepción inmediatamente sin intentar ejecutar la operación, previniendo así la sobrecarga. Después del tiempo de espera, el circuito pasa al estado HALF_OPEN
, permitiendo un único intento para verificar si el servicio se ha recuperado. Si el intento es exitoso, el circuito se cierra; de lo contrario, se vuelve a abrir.
<?php
// Ejemplo de uso
$circuitBreaker = new CircuitBreaker(3, 10); // Umbral de 3 fallos, reintento después de 10 segundos
$operation = function() {
// Simulación de una operación que falla aleatoriamente
if (rand(0, 2) === 0) {
throw new \Exception("Error en la operación.");
}
return "Operación exitosa.";
};
for ($i = 0; $i < 10; $i++) {
try {
$result = $circuitBreaker->call($operation);
echo "Resultado: " . $result . "\n";
} catch (\Exception $e) {
echo "Excepción: " . $e->getMessage() . "\n";
}
sleep(1);
}
En este ejemplo, creamos una instancia de CircuitBreaker
y definimos una operación que simula fallos aleatorios. Dentro de un bucle, intentamos ejecutar la operación usando $circuitBreaker->call()
. Si la operación tiene éxito, se muestra el resultado. Si falla y el circuito está cerrado, la excepción se captura y se muestra. Si el circuito está abierto, la excepción indicará que el circuito está abierto y se reintentará después del tiempo de espera definido. Este patrón proporciona una forma efectiva de gestionar errores transitorios y proteger la estabilidad de la aplicación.
No hay comentarios:
Publicar un comentario