Implementación de Rate Limiting Distribuido con Redis y PHP
El rate limiting, o limitación de velocidad, es una técnica crucial para proteger APIs y servicios web contra abusos, ataques DDoS y el consumo excesivo de recursos. Implementar un rate limiter robusto y escalable requiere una solución distribuida, especialmente en entornos con múltiples servidores. Este artículo explora cómo implementar un rate limiter distribuido utilizando Redis y PHP.
Redis, una base de datos en memoria, ofrece las características necesarias para una implementación eficiente de rate limiting distribuido, incluyendo operaciones atómicas y un rendimiento extremadamente rápido. Utilizaremos listas de Redis para rastrear las solicitudes dentro de un período de tiempo específico para cada usuario o IP.
<?php
// Configuración de Redis
$redis = new Redis();
$redis->connect('127.0.0.1', 6379);
/**
* Aplica rate limiting a una clave específica (por ejemplo, IP o ID de usuario).
*
* @param string $key La clave a la que se aplicará el rate limiting.
* @param int $limit El número máximo de solicitudes permitidas.
* @param int $periodo El período de tiempo en segundos dentro del cual se aplica el límite.
* @return bool True si la solicitud está permitida, False si se excede el límite.
*/
function aplicarRateLimit(string $key, int $limit, int $periodo): bool {
global $redis;
$claveRedis = "rate_limit:" . $key;
$timestampActual = time();
// Elimina las solicitudes antiguas de la lista
$redis->zRemRangeByScore($claveRedis, 0, $timestampActual - $periodo);
// Obtiene el número de solicitudes restantes
$conteoSolicitudes = $redis->zCard($claveRedis);
// Verifica si se ha excedido el límite
if ($conteoSolicitudes >= $limit) {
return false; // Límite excedido
}
// Agrega la solicitud actual a la lista
$redis->zAdd($claveRedis, $timestampActual, $timestampActual);
// Configura un tiempo de vida para la clave (opcional, para limpieza)
$redis->expire($claveRedis, $periodo * 2);
return true; // Solicitud permitida
}
// Ejemplo de uso: Limitar las solicitudes de una IP a 10 por minuto
$ipUsuario = $_SERVER['REMOTE_ADDR'];
$limite = 10;
$periodo = 60; // 60 segundos (1 minuto)
if (aplicarRateLimit($ipUsuario, $limite, $periodo)) {
// Procesar la solicitud
echo "Solicitud permitida.";
} else {
// Rechazar la solicitud
http_response_code(429); // Too Many Requests
echo "Demasiadas solicitudes. Inténtalo de nuevo más tarde.";
}
$redis->close();
Este código utiliza sorted sets de Redis para almacenar las marcas de tiempo de las solicitudes. La función `zRemRangeByScore` elimina las solicitudes antiguas, `zCard` cuenta las solicitudes actuales y `zAdd` agrega una nueva solicitud. Al usar sorted sets, podemos eficientemente contar y eliminar solicitudes basándonos en su marca de tiempo. La función `aplicarRateLimit` devuelve `true` si la solicitud está permitida y `false` en caso contrario, lo que permite rechazar solicitudes y devolver un código de estado HTTP 429 (Too Many Requests).
Recuerda ajustar los parámetros de configuración de Redis (`host`, `port`) según tu entorno. Además, considera implementar un middleware o un interceptor en tu framework PHP para aplicar el rate limiting de forma centralizada.
No hay comentarios:
Publicar un comentario