La limitación de velocidad, también conocida como estrangulamiento de solicitudes, es un mecanismo para controlar la tasa a la que un usuario o servicio puede enviar solicitudes a una API o servidor, previniendo el abuso, asegurando una asignación justa de recursos y manteniendo la estabilidad del sistema.
Comprensión de la Limitación de Velocidad
La limitación de velocidad protege los servicios de volúmenes excesivos de solicitudes que podrían conducir a la degradación del rendimiento, ataques de denegación de servicio (DoS) o agotamiento de recursos. Asegura que los recursos del sistema estén disponibles para todos los usuarios legítimos y evita que una sola entidad monopolice el acceso. Un servicio de proxy a menudo desempeña un papel crucial en la aplicación de estos límites, ya sea aplicándolos a las solicitudes del cliente antes de que lleguen a los servicios ascendentes o comunicando los límites ascendentes a los clientes.
¿Por qué implementar la limitación de velocidad?
- Protección de Recursos: Evita que los servidores se saturen con demasiadas solicitudes, preservando la CPU, la memoria y el ancho de banda de la red.
- Prevención de Abusos: Mitiga ataques de fuerza bruta, relleno de credenciales y otras actividades maliciosas al limitar los intentos de solicitud.
- Uso Justo: Asegura que todos los clientes reciban un acceso equitativo a los recursos compartidos, evitando que un solo cliente monopolice el sistema.
- Control de Costos: Para servicios con facturación basada en el uso, los límites de velocidad pueden ayudar a controlar los costos operativos al limitar el consumo de recursos.
Algoritmos Comunes de Limitación de Velocidad
Se emplean diferentes algoritmos para rastrear y aplicar límites de velocidad, cada uno con características distintas en cuanto a cómo manejan las ráfagas y el uso de recursos.
Cubo de Tokens (Token Bucket)
El algoritmo Cubo de Tokens modela un cubo con una capacidad fija que se rellena con tokens a una velocidad constante. Cada solicitud consume un token. Si el cubo está vacío, la solicitud es rechazada o puesta en cola. Este algoritmo permite cierta ráfaga, ya que las solicitudes pueden consumir múltiples tokens si están disponibles, hasta la capacidad del cubo.
Cubo con Fugas (Leaky Bucket)
El algoritmo Cubo con Fugas procesa las solicitudes a una tasa de salida fija. Las solicitudes se añaden a una cola (el "cubo"). Si la cola está llena, las nuevas solicitudes son rechazadas. Las solicitudes "se escapan" del cubo a una tasa constante, asegurando un flujo constante de procesamiento. Este algoritmo suaviza las ráfagas pero introduce latencia para las solicitudes que deben esperar en la cola.
Contador de Ventana Fija (Fixed Window Counter)
En el algoritmo Contador de Ventana Fija, se define una ventana de tiempo (por ejemplo, 60 segundos) y un contador rastrea las solicitudes dentro de esa ventana. Una vez que la ventana expira, el contador se reinicia. Las solicitudes que exceden el límite dentro de la ventana son rechazadas. Un inconveniente es el "problema de ráfaga" en los bordes de la ventana, donde los clientes podrían enviar el doble de las solicitudes permitidas en dos ventanas consecutivas.
Registro de Ventana Deslizante (Sliding Window Log)
El algoritmo Registro de Ventana Deslizante registra una marca de tiempo para cada solicitud. Cuando llega una nueva solicitud, el sistema cuenta el número de marcas de tiempo dentro de los últimos N segundos (la ventana). Si este recuento excede el límite, la solicitud es rechazada. Este método es preciso pero puede consumir mucha memoria debido al almacenamiento de todas las marcas de tiempo.
Contador de Ventana Deslizante (Sliding Window Counter)
Este algoritmo combina aspectos de Ventana Fija y Registro de Ventana Deslizante para mitigar el problema del borde sin la sobrecarga de memoria de registrar cada solicitud. Utiliza dos ventanas fijas: la ventana actual y la ventana anterior. La marca de tiempo de la solicitud actual determina su posición dentro de la ventana actual. Las solicitudes permitidas se calculan como un promedio ponderado del recuento de la ventana anterior y el recuento de la ventana actual, basado en la fracción de la ventana actual que ha transcurrido.
Comparación de Algoritmos
| Característica | Cubo de Tokens | Cubo con Fugas | Contador de Ventana Fija | Registro de Ventana Deslizante | Contador de Ventana Deslizante |
|---|---|---|---|---|---|
| Manejo de Ráfagas | Permite ráfagas | Suaviza ráfagas | Susceptible a ráfagas | Maneja bien las ráfagas | Maneja bien las ráfagas |
| Uso de Recursos | Moderado | Moderado | Bajo | Alto (memoria) | Bajo a Moderado |
| Complejidad | Moderada | Moderada | Baja | Alta | Moderada |
| Precisión | Buena | Buena | Pobre (casos límite) | Alta | Buena |
| Impacto en Latencia | Bajo (si hay tokens) | Alto (en cola) | Bajo | Bajo | Bajo |
Identificación de la Limitación de Velocidad
Cuando se excede un límite de velocidad, una API o servicio suele responder con códigos de estado HTTP y encabezados específicos.
Código de Estado HTTP 429 Demasiadas Solicitudes
El código de estado HTTP estándar para la limitación de velocidad es 429 Too Many Requests (Demasiadas Solicitudes). Esto indica que el usuario ha enviado demasiadas solicitudes en un período de tiempo determinado.
HTTP/1.1 429 Too Many Requests
Retry-After: 30
Content-Type: application/json
{
"error": "Rate limit exceeded. Try again in 30 seconds."
}
Encabezados de Respuesta
Las API a menudo incluyen encabezados específicos para proporcionar más contexto sobre el estado del límite de velocidad y cómo manejarlo.
Retry-After: (RFC 7231, Sección 7.1.3) Indica cuánto tiempo debe esperar el agente de usuario antes de realizar una solicitud de seguimiento. Su valor puede ser un entero que representa segundos o una fecha/hora específica.X-RateLimit-Limit: El número máximo de solicitudes permitidas en la ventana de límite de velocidad actual.X-RateLimit-Remaining: El número de solicitudes restantes en la ventana de límite de velocidad actual.X-RateLimit-Reset: El tiempo (generalmente segundos de época Unix) en que se reinicia la ventana de límite de velocidad actual.
Estos encabezados X-RateLimit-* son comunes pero no están estandarizados por un RFC; su nombre y comportamiento exactos pueden variar entre servicios.
Manejo de Límites de Velocidad
El manejo efectivo de los límites de velocidad por parte del cliente es crucial para construir aplicaciones robustas que interactúen con servicios externos.
Retroceso Exponencial con Jitter
Esta es una estrategia estándar para reintentar solicitudes fallidas, incluidas las debidas a la limitación de velocidad.
- Retroceso Exponencial: El cliente espera una cantidad de tiempo que aumenta exponencialmente entre reintentos (por ejemplo, 1 segundo, luego 2 segundos, luego 4 segundos, luego 8 segundos).
- Jitter (Variación Aleatoria): Se añade un pequeño retraso aleatorio al período de retroceso. Esto evita que todos los clientes reintenten simultáneamente después de un reinicio del límite de velocidad, lo que podría desencadenar otra ola de limitación de velocidad.
import time
import random
import requests
def make_request_with_retry(url, max_retries=5):
retries = 0
while retries < max_retries:
try:
response = requests.get(url)
if response.status_code == 429:
retry_after = int(response.headers.get('Retry-After', 1))
print(f"Rate limited. Retrying after {retry_after} seconds.")
time.sleep(retry_after)
elif 200 <= response.status_code < 300:
return response
else:
response.raise_for_status() # Raise an exception for other HTTP errors
except requests.exceptions.RequestException as e:
print(f"Request failed: {e}")
# Exponential backoff with jitter
delay = (2 ** retries) + random.uniform(0, 1) # 2^retries + random float between 0 and 1
print(f"Retrying in {delay:.2f} seconds...")
time.sleep(delay)
retries += 1
raise Exception(f"Failed to make request after {max_retries} retries.")
# Example usage:
# response = make_request_with_retry("https://api.example.com/data")
# if response:
# print("Request successful:", response.json())
Respetar el Encabezado Retry-After
Si una API proporciona un encabezado Retry-After, los clientes deben respetar esta directiva. El valor especifica el tiempo mínimo de espera antes de enviar otra solicitud al mismo endpoint.
Caché del Lado del Cliente
Almacene en caché las respuestas de endpoints frecuentemente accedidos y no volátiles. Esto reduce el número de solicitudes enviadas a la API, ayudando indirectamente a mantenerse dentro de los límites de velocidad.
Agrupación de Solicitudes (Batching Requests)
Si la API lo admite, combine múltiples operaciones más pequeñas en una única solicitud más grande. Esto reduce el número total de llamadas a la API.
Estrangulamiento Predictivo
Los clientes pueden monitorear su propia tasa de solicitudes y ralentizar o pausar proactivamente las solicitudes a medida que se acercan a los límites de velocidad conocidos, en lugar de esperar una respuesta 429. Esto requiere conocer de antemano los límites de velocidad de la API.
Configuración del Servicio de Proxy para la Limitación de Velocidad
Un servicio de proxy robusto ofrece características completas para gestionar los límites de velocidad, tanto para los clientes que acceden a los servicios a través del proxy como para las propias interacciones del proxy con las API ascendentes.
Aplicación de Límites en el Tráfico de Entrada (Ingress Traffic)
El proxy puede aplicar límites de velocidad a las solicitudes de clientes entrantes basándose en varios criterios.
- Dirección IP del Cliente: Limita las solicitudes de una sola IP.
- Clave/Token de API: Limita las solicitudes asociadas con una credencial de autenticación específica.
- ID de Usuario: Si el proxy puede extraer información del usuario de encabezados o tokens.
- Ruta/Endpoint: Diferentes límites de velocidad para diferentes endpoints de API (por ejemplo,
/searchpodría tener un límite más alto que/admin/delete).
# Ejemplo: Configuración de proxy para limitación de velocidad por IP
http:
routers:
api-router:
rule: "Host(`api.example.com`)"
service: api-service
middlewares: [rate-limit-ip]
middlewares:
rate-limit-ip:
rateLimit:
average: 100 # solicitudes por segundo
burst: 50 # ráfaga máxima más allá del promedio
sourceCriterion: "ipStrategy" # Aplicar límite por IP de origen
Gestión del Tráfico de Salida (Egress Traffic) a Servicios Ascendentes
Cuando el propio proxy consume API ascendentes, puede implementar su propia limitación de velocidad para evitar saturar esos servicios externos. Esto es crítico para escenarios de integración donde el proxy agrega datos de múltiples fuentes.
- Límites Específicos del Servicio Ascendente: Configure límites de velocidad distintos para cada servicio ascendente con el que se comunica el proxy.
- Interrupción de Circuito (Circuit Breaking): Combine la limitación de velocidad con patrones de interrupción de circuito para aislar fallos cuando un servicio ascendente deja de responder o limita consistentemente la velocidad del proxy.
Personalización y Granularidad
Las configuraciones avanzadas de proxy permiten un control granular sobre la limitación de velocidad:
- Límites Dinámicos: Ajuste los límites basándose en la salud del backend, la hora del día u otras métricas operativas.
- Límites por Niveles: Implemente diferentes límites de velocidad para diferentes niveles de clientes (por ejemplo, usuarios gratuitos frente a premium).
- Gestión de Cuotas: Rastree el uso en función de cuotas a largo plazo (por ejemplo, solicitudes por mes), además de los límites de velocidad a corto plazo.
Monitoreo y Alertas
Un servicio de proxy debe proporcionar herramientas para monitorear las estadísticas de limitación de velocidad:
- Recuentos de Solicitudes: Rastree el total de solicitudes, solicitudes exitosas y solicitudes con limitación de velocidad.
- Violaciones de Límites: Alerte cuando los límites de velocidad se estén acercando o excediendo para clientes o servicios ascendentes específicos.
- Tendencias de Uso: Visualice los patrones de solicitud a lo largo del tiempo para identificar posibles cuellos de botella o abusos.
El monitoreo ayuda a los equipos de operaciones a comprender los patrones de tráfico, optimizar las configuraciones de límites de velocidad y abordar proactivamente los problemas antes de que afecten la disponibilidad del servicio.