miércoles, 25 de junio de 2025

Implementación de Locks Distribuídos con Redis para Sincronización en PHP

Implementación de Locks Distribuídos con Redis para Sincronización en PHP

En entornos distribuidos, la necesidad de sincronizar el acceso a recursos compartidos se vuelve crucial para evitar condiciones de carrera y garantizar la integridad de los datos. Una solución popular es el uso de locks distribuidos. Redis, gracias a su atomicidad y bajo tiempo de latencia, se presenta como una excelente opción para implementar estos locks en aplicaciones PHP.

Este artículo mostrará cómo implementar un lock distribuido básico con Redis utilizando la extensión phpredis. Utilizaremos las funciones setnx (SET if Not eXists) y del (DELETE) para adquirir y liberar el lock, respectivamente. Un aspecto importante es incluir un tiempo de expiración al lock para evitar que, en caso de fallo del proceso que lo adquirió, el recurso quede bloqueado indefinidamente.


<?php

class DistributedLock {

    private $redis;
    private $lockKeyPrefix = 'lock:';

    public function __construct(Redis $redis) {
        $this->redis = $redis;
    }

    public function acquireLock(string $resourceId, int $ttl = 10): bool {
        $lockKey = $this->lockKeyPrefix . $resourceId;
        $lockValue = uniqid('', true); // Valor único para identificar el lock
        $acquired = $this->redis->setnx($lockKey, $lockValue);

        if ($acquired) {
            $this->redis->expire($lockKey, $ttl); // Establecer el tiempo de vida (TTL)
            return true;
        } else {
            return false;
        }
    }

    public function releaseLock(string $resourceId): bool {
        $lockKey = $this->lockKeyPrefix . $resourceId;
        return $this->redis->del($lockKey) > 0;
    }
}

// Ejemplo de uso:
$redis = new Redis();
$redis->connect('127.0.0.1', 6379); // Reemplaza con tu configuración de Redis

$lock = new DistributedLock($redis);
$resourceId = 'my_resource';

if ($lock->acquireLock($resourceId)) {
    try {
        // Sección crítica: Aquí realizamos las operaciones que requieren sincronización.
        echo "Lock adquirido. Procesando recurso...\n";
        sleep(5); // Simula un proceso que tarda.
        echo "Recurso procesado.\n";
    } finally {
        if ($lock->releaseLock($resourceId)) {
            echo "Lock liberado.\n";
        } else {
            echo "Error al liberar el lock.\n";
        }
    }
} else {
    echo "No se pudo adquirir el lock. El recurso está bloqueado.\n";
}

$redis->close();
?>
    

El código anterior define una clase DistributedLock que encapsula la lógica para adquirir y liberar el lock. La función acquireLock intenta establecer una clave en Redis usando setnx. Si la clave no existe, se crea y se establece un tiempo de expiración. La función releaseLock simplemente elimina la clave de Redis.

Es importante notar que este es un ejemplo básico. Para una implementación más robusta, se deben considerar escenarios como la comprobación del valor del lock antes de liberarlo (para asegurar que el proceso actual es el que adquirió el lock) y la implementación de un mecanismo de reintento en la adquisición del lock.

No hay comentarios:

Publicar un comentario