Marcio Cunha

Aislamiento de Microservicios y Limitación de Tráfico con Cubo de Tokens

Aprenda a proteger microservicios contra sobrecargas utilizando el algoritmo de cubo de fichas distribuido y capas de aislamiento para garantizar alta disponibilidad.

Marcio Cunha•5 min
También disponible en:EnglishPortuguês
Resumen
  • El algoritmo de cubo de fichas funciona como un contenedor que almacena tokens de permiso reabastecidos a tasas constantes para regular el tráfico.
  • Los sistemas distribuidos requieren sincronización atómica de contadores mediante Redis o scripts de Lua para evitar inconsistencias globales.
  • El aislamiento de fallos por compartimento estanco evita que un servicio inestable arrastre a toda la arquitectura de microservicios.
  • La latencia de red introducida por verificaciones externas de límites de solicitudes requiere estrategias de caché local en memoria.
  • Los mecanismos de contrapresión y cortafuegos complementan el control de flujo al rechazar llamadas antes de que ocurran fallos en cascada.

El desafío de mantener microservicios estables bajo presión

Cuando construimos sistemas basados en microservicios, dividimos una aplicación grande en piezas más pequeñas que se comunican entre sí a través de la red. En la práctica, esto significa que un solo clic de usuario en la interfaz puede desencadenar docenas de llamadas internas en cadena entre diferentes servidores. Si uno de estos pequeños servicios comienza a fallar o recibe tráfico excesivo, puede bloquearse y arrastrar a los demás con él, creando un efecto dominó catastrófico en la infraestructura. Para evitar que este colapso ocurra, los ingenieros recurren a dos estrategias fundamentales: capas de aislamiento y control riguroso de tráfico.

El aislamiento de fallos funciona como los compartimentos estancos de un barco, que impiden que una vía de agua hunda toda la embarcación. En el mundo del software, esto significa limitar el impacto de un error para que quede contenido únicamente en la parte afectada del sistema. Por otro lado, el control de tráfico, conocido en ingeniería como rate limiting, actúa como un guardia de seguridad en la puerta de un evento concurrido, controlando exactamente cuántas personas pueden entrar por minuto. Combinar estos dos enfoques en entornos distribuidos requiere herramientas inteligentes que logren contar solicitudes de manera precisa, incluso cuando el sistema opera disperso en docenas de máquinas diferentes.

Entendiendo el funcionamiento del algoritmo Token Bucket

Para controlar el flujo de solicitudes sin bloquear el tráfico legítimo de forma abrupta, utilizamos algoritmos matemáticos específicos, siendo el Token Bucket uno de los más populares y eficientes de la computación. En la práctica, imagine un cubo imaginario que recibe fichas (tokens) cada segundo, hasta alcanzar una capacidad máxima. Cada vez que un usuario realiza una solicitud al servidor, el sistema retira una ficha de este cubo; si el cubo está completamente vacío, la solicitud es rechazada o colocada en una cola de espera hasta que lleguen nuevas fichas.

La gran ventaja de este enfoque en comparación con métodos más rígidos es su flexibilidad para manejar picos repentinos de acceso legítimo. Si un usuario pasa unos minutos sin usar el sistema, su cubo estará lleno, lo que le permitirá ejecutar múltiples acciones a la vez sin sufrir interrupciones. Cuando el cubo se vacía durante un uso intenso, el sistema no bloquea totalmente el acceso, sino que pasa a aceptar solicitudes solo a la misma velocidad en que se generan nuevas fichas, garantizando un flujo constante y predecible.

Implementación distribuida con Redis y scripts de Lua

Cuando la aplicación se ejecuta en un solo servidor, contar fichas en un cubo es una tarea sencilla almacenada en la memoria RAM local. Sin embargo, en la arquitectura moderna de microservicios, ejecutamos cientos de instancias de la misma aplicación detrás de un balanceador de carga, lo que significa que el cubo de fichas debe ser compartido por todas ellas. En la práctica, esto crea un problema complejo de concurrencia, donde dos solicitudes que llegan al mismo milisegundo en servidores diferentes podrían leer el mismo número de fichas y gastarlas simultáneamente.

Para resolver este problema de sincronización sin perder rendimiento, utilizamos bases de datos en memoria ultrarrápidas como Redis, combinadas con scripts ejecutados directamente en el servidor de datos utilizando el lenguaje Lua. El script en Lua garantiza atomicidad, lo que significa que la verificación de las fichas, la sustracción y la actualización del tiempo ocurren en una sola operación indivisible. A continuación, visualizamos la estructura lógica de un script básico utilizado para gestionar este control de forma distribuida entre varias instancias:

local key = KEYS[1]local capacity = tonumber(ARGV[1])local fill_rate = tonumber(ARGV[2])local now = tonumber(ARGV[3])local requested = tonumber(ARGV[4])local current_bucket = redis.call('HMGET', key, 'tokens', 'last_updated')local tokens = tonumber(current_bucket[1])local last_updated = tonumber(current_bucket[2])if not tokens then tokens = capacitylast_updated = nowelselocal delta = math.max(0, now - last_updated)tokens = math.min(capacity, tokens + (delta * fill_rate))endredis.call('HMSET', key, 'tokens', tokens, 'last_updated', now)return tokens

Aislamiento de recursos y prevención de fallos en cascada

Controlar cuántas solicitudes entran al sistema es solo la mitad del camino para garantizar estabilidad, ya que también necesitamos proteger los servicios internos contra ralentizaciones puntuales. En la práctica, el patrón conocido como bulkhead (compartimento estanco) separa los recursos de computación — como conexiones de bases de datos y hilos de procesamiento — en piscinas aisladas para cada dependencia externa. Si el servicio de pagos comienza a responder con lentitud debido a problemas en el proveedor de tarjetas, consumirá únicamente su propia cuota de hilos, dejando intactas las reservas dedicadas al catálogo de productos y al inicio de sesión de usuarios.

Esta separación física y lógica evita el agotamiento total de recursos de la máquina, un fenómeno común donde la lentitud en un único componente secundario paraliza el servidor entero. Cuando combinamos el aislamiento de hilos con cortafuegos de software (circuit breakers), que interrumpen automáticamente llamadas a servicios que ya han demostrado estar fuera de servicio, creamos una red de seguridad resiliente. El sistema aprende a fallar rápido y de forma controlada, preservando la experiencia de los clientes incluso cuando partes de la infraestructura enfrentan inestabilidades graves.

Consideraciones operacionales y conclusión

Implementar limitación de tráfico distribuida y capas de aislamiento requiere una planificación cuidadosa sobre dónde posicionar estas barreras en la arquitectura de software. Aunque el uso de herramientas centralizadas como Redis garantiza precisión matemática en el control de tráfico, introduce una dependencia de red adicional que puede añadir algunos milisegundos de latencia en cada solicitud. Por ello, los equipos de ingeniería frecuentemente combinan verificaciones locales en memoria con validaciones asíncronas periódicas en el servidor central, encontrando el equilibrio ideal entre rendimiento bruto y rigor en la gobernanza.

En última instancia, la construcción de sistemas distribuidos resilientes no se resume solo a escribir código funcional, sino a anticipar escenarios de fallo y planificar cómo debe comportarse el software bajo estrés extremo. La adopción consciente de algoritmos como Token Bucket, combinada con sólidas estrategias de aislamiento de recursos, transforma arquitecturas frágiles en ecosistemas robustos capaces de absorber picos de acceso y fallos parciales sin perder la compostura operacional.