Marcio Cunha

Limitación de Tráfico Distribuida con Redis Cluster y Token Bucket

Aprende a diseñar sistemas de control de tráfico a gran escala utilizando Redis Cluster y el algoritmo Token Bucket para proteger APIs contra sobrecargas.

Marcio Cunha•5 min
También disponible en:EnglishPortuguês
Resumen
  • El algoritmo Token Bucket equilibra picos de tráfico y consumo sostenido sin descartar solicitudes legítimas prematuramente.
  • La fragmentación de datos en Redis Cluster previene cuellos de botella de memoria y CPU cuando el volumen de peticiones alcanza millones por segundo.
  • Los scripts de Lua atómicos eliminan condiciones de carrera en operaciones concurrentes de lectura y escritura en la base de datos en memoria.
  • La latencia de red entre nodos distribuidos exige estrategias de respaldo tolerantes a fallos para garantizar disponibilidad continua.
  • El monitoreo continuo de claves calientes previene desequilibrios severos de carga entre los fragmentos del clúster Redis.

El Desafío de Proteger APIs en Entornos Distribuidos

Cuando múltiples servidores procesan miles de millones de solicitudes simultáneas, proteger la infraestructura contra abusos se convierte en un requisito vital de ingeniería. En la práctica, esto significa evitar que scrapers maliciosos, errores de software o picos repentinos de acceso derriben los servicios principales, garantizando estabilidad para los usuarios legítimos. La limitación de velocidad actúa como un portero inteligente que decide quién entra y quién debe esperar en la fila.

En arquitecturas modernas basadas en microservicios, esta tarea deja de ser sencilla. Como los nodos de aplicación escalan horizontalmente y se ejecutan en servidores diferentes, mantener el control global del número de peticiones requiere un componente centralizado y extremadamente rápido. Sin una coordinación eficiente, los sistemas fallan al contabilizar el tráfico total, permitiendo que los clientes superen los límites establecidos simplemente alternando entre diferentes instancias de la API.

Entendiendo el Algoritmo Token Bucket en la Práctica

Existen varias formas matemáticas de controlar el flujo de datos, pero el algoritmo Token Bucket destaca por su flexibilidad. Imagine un cubo físico que recibe agua a un ritmo constante, digamos diez gotas por segundo, hasta alcanzar su capacidad máxima. Cada solicitud que llega toma una gota de este cubo para poder ser procesada por el sistema.

Si el usuario realiza una ráfaga rápida de peticiones, el cubo puede atenderla instantáneamente siempre que haya suficientes tokens acumulados dentro de él. Cuando el cubo se vacía por completo, las nuevas solicitudes comienzan a ser rechazadas o puestas en espera hasta que pase el tiempo y se generen nuevos tokens. En la práctica, este comportamiento permite picos legítimos de uso sin penalizar al usuario, al tiempo que protege el backend contra sobrecargas continuas.

Por qué Redis Cluster es la Opción Ideal

Para implementar esta lógica a gran escala, necesitamos un almacenamiento de datos ultra rápido que admita millones de operaciones por segundo con latencia en el rango de los microsegundos. Redis cumple perfectamente con esta necesidad al mantener todos los datos en la memoria RAM, eliminando la lentitud típica de los discos duros tradicionales. En entornos corporativos de tráfico altísimo, un solo nodo de Redis puede saturar la CPU o alcanzar el límite físico de memoria.

Aquí es donde entra Redis Cluster, una topología distribuida que divide los datos en múltiples fragmentos llamados shards distribuidos en varios servidores. Con este enfoque, la carga de trabajo se distribuye de manera inteligente, permitiendo que el sistema escale horizontalmente a medida que aumenta el tráfico de la aplicación. Sin embargo, coordinar claves distribuidas trae nuevos desafíos de consistencia que deben manejarse directamente a nivel de código.

Garantizando Atomicidad con Scripts de Lua

Uno de los mayores peligros en los sistemas concurrentes es la condición de carrera, que ocurre cuando dos peticiones leen y modifican el mismo saldo de tokens exactamente en el mismo microsegundo. Sin un mecanismo de bloqueo, el sistema puede calcular erróneamente los tokens restantes, permitiendo accesos indebidos. Para resolver este problema sin perjudicar el rendimiento, utilizamos scripts escritos en el lenguaje Lua ejecutados directamente dentro de Redis.

Redis ejecuta scripts de Lua de forma estrictamente atómica, lo que significa que ninguna otra operación puede interrumpir el código mientras se está ejecutando. En la práctica, el script calcula el tiempo transcurrido, repone los tokens debidos en el cubo, verifica si hay suficiente saldo para la petición actual y actualiza el estado en una sola transacción indivisible. A continuación se muestra un ejemplo práctico de implementación de este script en un entorno Node.js utilizando Redis:

const Redis = require('ioredis');
const redis = new Redis.Cluster([{ host: '127.0.0.1', port: 7000 }]);

const tokenBucketScript = `
  local key = KEYS[1]
  local capacity = tonumber(ARGV[1])
  local fillRate = tonumber(ARGV[2])
  local requested = tonumber(ARGV[3])
  local now = tonumber(ARGV[4])

  local bucket = redis.call('hmget', key, 'tokens', 'last_updated')
  local tokens = tonumber(bucket[1])
  local last_updated = tonumber(bucket[2])

  if not tokens then
    tokens = capacity
    last_updated = now
  else
    local elapsed = math.max(0, now - last_updated)
    tokens = math.min(capacity, tokens + (elapsed * fillRate))
    last_updated = now
  end

  if tokens < requested then
    return {0, tokens}
  else
    tokens = tokens - requested
    redis.call('hmset', key, 'tokens', tokens, 'last_updated', last_updated)
    return {1, tokens}
  end
`;

async function checkRateLimit(userId, capacity, fillRate, cost) {
  const now = Math.floor(Date.now() / 1000);
  const result = await redis.eval(tokenBucketScript, 1, `rate:{userId}`, capacity, fillRate, cost, now);
  return result[0] === 1;
}

Mitigando Fallos y Gestión de Claves Calientes

Aun con una arquitectura robusta, ocurren imprevistos y los nodos de Redis Cluster pueden fallar durante los picos de acceso. Para evitar que la indisponibilidad del caché tire toda la API, es fundamental implementar políticas de respaldo adecuadas. En la práctica, si el clúster Redis deja de responder por cualquier motivo, el middleware de control de tráfico debe permitir el paso temporal de las solicitudes, priorizando la disponibilidad del negocio sobre el bloqueo estricto.

Otro problema crítico es el fenómeno de las claves calientes, que ocurre cuando un solo usuario o recurso genera un volumen masivo de solicitudes concentradas en un solo shard del clúster. Como Redis Cluster distribuye las claves mediante hash, las claves con el mismo prefijo pueden caer en el mismo nodo, creando un cuello de botella. El uso de técnicas de etiquetado de hash, que fuerzan a ciertas claves a caer en nodos alternativos, ayuda a repartir el esfuerzo computacional y mantener el sistema estable.

Consideraciones Finales

Construir un sistema de limitación de tráfico distribuido requiere equilibrar precisión matemática, rendimiento de red y resiliencia operativa. El uso combinado de Redis Cluster con el algoritmo Token Bucket garantiza que las aplicaciones de alta concurrencia puedan absorber tráfico intenso sin comprometer la integridad de los servidores backend.

Comprender las compensaciones involucradas en la atomicidad de los scripts y el manejo de fallos permite a los ingenieros diseñar sistemas escalables preparados para soportar el crecimiento explosivo de usuarios reales. La elección consciente de herramientas y el monitoreo continuo de los nodos forman la base indispensable para mantener cualquier servicio digital seguro y disponible.