Estrategias de Limitación de Velocidad para APIs
La limitación de velocidad controla la frecuencia de solicitudes de clientes, protegiend APIs del abuso, asegurando asignación justa de recursos y previniendo fallos en cascada. Diferentes algoritmos e implementaciones ofrecen niveles variados de precisión, rendimiento y complejidad operacional. Esta guía cubre algoritmos de token bucket y ventana deslizante, limitación de velocidad de Nginx y HAProxy, análisis de encabezado de solicitud, límites por cliente y estándares de encabezado de respuesta de velocidad.
Tabla de Contenidos
- Descripción General de Limitación de Velocidad
- Algoritmo Token Bucket
- Algoritmo Ventana Deslizante
- Algoritmo Leaky Bucket
- Limitación de Velocidad de Nginx
- Limitación de Velocidad de HAProxy
- Análisis de Encabezado de Solicitud
- Límites de Velocidad Por Cliente
- Encabezados de Respuesta de Límite de Velocidad
- Limitación de Velocidad Distribuida
- Pruebas y Monitoreo
Descripción General de Limitación de Velocidad
Estrategias de limitación de velocidad:
- Por IP: Limitar por dirección IP del cliente
- Por Usuario: Limitar por usuario autenticado
- Por Clave API: Limitar por clave de API
- Por Endpoint: Diferentes límites para diferentes endpoints
- Distribuida: Estado compartido a través de múltiples servidores
Algoritmos comunes:
- Token Bucket: Acumula tokens, consume uno por solicitud
- Ventana Deslizante: Cuenta solicitudes en ventana de tiempo
- Leaky Bucket: Encola solicitudes, filtra a velocidad fija
- Ventana Fija: Contador simple por período de tiempo
Beneficios:
- Previene abuso de API y ataques DDoS
- Asegura uso justo de recursos
- Protege infraestructura backend
- Mejora estabilidad del servicio
Costos:
- Latencia agregada (especialmente distribuida)
- Complejidad en implementación
- Costo de almacenamiento para rastreo
- Potencial para falsos positivos
Algoritmo Token Bucket
Token bucket mantiene un cubo de tokens, consumiendo uno por solicitud:
Algoritmo:
1. Comience con N tokens
2. Cada segundo, agregue R tokens (hasta máximo N)
3. Cada solicitud consume 1 token
4. Rechace solicitudes cuando cubo esté vacío
5. Los tokens no utilizados se acumulan (capacidad de ráfaga)
Ventajas:
- Permite tráfico de ráfaga (búfer de tokens)
- Simple de implementar
- Comportamiento predecible
- Costo bajo de CPU
Ejemplo: 100 solicitudes/seg con 50 capacidad de ráfaga
Inicial: 50 tokens
Segundo 1: 50 + 100 = 150 (limitado a 150) → 49 tokens después de solicitudes
Segundo 2: 49 + 100 = 149 (limitado a 150) → 48 tokens después de solicitudes
Algoritmo Ventana Deslizante
La ventana deslizante cuenta solicitudes en una ventana de tiempo móvil:
Algoritmo:
1. Mantenga conteo de solicitudes en últimos T segundos
2. Para nueva solicitud, compruebe si conteo < límite
3. Agregue timestamp de solicitud
4. Elimine timestamps más antiguos que T segundos
5. Permita si conteo < límite
Ventajas:
- Limitación de velocidad justa (no ventanas fijas)
- Sin capacidad de ráfaga (límites estrictos)
- Previene comportamiento de caso límite
Desventajas:
- Uso de memoria más alto
- Más intensivo en CPU
- Requiere rastreo preciso de timestamp
Ejemplo: 100 solicitudes por 60 segundos
Tiempo 0.00s: Solicitud 1 → [0.00] → conteo=1 ✓
Tiempo 0.05s: Solicitud 2 → [0.00, 0.05] → conteo=2 ✓
...
Tiempo 1.00s: Solicitud 100 → [0.00, 0.05, ..., 1.00] → conteo=100 ✓
Tiempo 1.01s: Solicitud 101 → Timestamps antiguos eliminados, conteo < 100 ✓
Tiempo 0.05s (eliminado): conteo cae, solicitudes nuevas permitidas
Algoritmo Leaky Bucket
Leaky bucket encola solicitudes y procesa a velocidad fija:
Algoritmo:
1. Encole solicitudes entrantes
2. Procese solicitudes de cola a velocidad fija R
3. Si cola llena, rechace solicitudes nuevas
4. Suavice ráfagas de tráfico en flujo constante
Ventajas:
- Velocidad de salida suave
- Uso de recursos predecible
- Previene sobrecarga de ráfaga
Desventajas:
- Latencia más alta para solicitudes iniciales
- Costo de gestión de cola
- No apropiado para cargas de trabajo de ráfaga
Limitación de Velocidad de Nginx
Nginx proporciona limitación de velocidad de token bucket:
Limitación de Velocidad Básica
# Define rate limit zones
limit_req_zone $binary_remote_addr zone=api_limit:10m rate=100r/s;
limit_req_zone $http_x_api_key zone=api_key_limit:10m rate=1000r/s;
server {
listen 80;
server_name api.example.com;
# Apply rate limit to entire API
limit_req zone=api_limit burst=50 nodelay;
location /api {
proxy_pass http://backend;
}
}
Parámetros:
$binary_remote_addr: IP del cliente (representación binaria)zone=nombre:tamaño: Nombre y tamaño de zona (10m = 10 megabytes)rate=100r/s: 100 solicitudes por segundoburst=50: Permita 50 solicitudes de ráfaga (cola)nodelay: No retrase solicitudes de ráfaga
Limitación de Velocidad Por Endpoint
limit_req_zone $binary_remote_addr zone=strict_limit:10m rate=10r/s;
limit_req_zone $binary_remote_addr zone=normal_limit:10m rate=100r/s;
server {
listen 80;
server_name api.example.com;
# Strict limit for expensive endpoints
location /api/expensive {
limit_req zone=strict_limit burst=5;
proxy_pass http://backend;
}
# Normal limit for standard endpoints
location /api/standard {
limit_req zone=normal_limit burst=20;
proxy_pass http://backend;
}
# High limit for fast endpoints
location /api/fast {
# No rate limit
proxy_pass http://backend;
}
}
Limitación de Velocidad por Clave de API
# Extract API key from header
map $http_x_api_key $api_client {
default "unknown";
"~^key_(.+)$" $1;
}
limit_req_zone $api_client zone=api_key_limit:10m rate=1000r/s;
server {
listen 80;
server_name api.example.com;
location /api {
# Check for valid API key
if ($http_x_api_key = "") {
return 401 '{"error": "Missing API key"}';
}
limit_req zone=api_key_limit burst=100 nodelay;
proxy_pass http://backend;
proxy_set_header X-API-Key $http_x_api_key;
}
}
Exclusiones de Lista Blanca
geo $whitelist {
default 0;
10.0.0.0/8 1;
192.168.0.0/16 1;
203.0.113.0/24 1; # Partner network
}
limit_req_zone $binary_remote_addr zone=api_limit:10m rate=100r/s;
server {
listen 80;
location /api {
if ($whitelist = 1) {
access_log off;
# Skip rate limiting for whitelisted IPs
proxy_pass http://backend;
}
limit_req zone=api_limit burst=50;
proxy_pass http://backend;
}
}
Límites de Velocidad Dinámicos
# Map user tier to rate limit
map $http_x_user_tier $rate_limit_zone {
"premium" $binary_remote_addr:premium;
"standard" $binary_remote_addr:standard;
default $binary_remote_addr:free;
}
limit_req_zone $rate_limit_zone zone=premium_limit:10m rate=5000r/s;
limit_req_zone $rate_limit_zone zone=standard_limit:10m rate=500r/s;
limit_req_zone $rate_limit_zone zone=free_limit:10m rate=50r/s;
server {
listen 80;
server_name api.example.com;
location /api {
if ($http_x_user_tier = "premium") {
limit_req zone=premium_limit burst=500;
}
if ($http_x_user_tier = "standard") {
limit_req zone=standard_limit burst=50;
}
if ($http_x_user_tier = "free") {
limit_req zone=free_limit burst=5;
}
proxy_pass http://backend;
}
}
Respuesta Personalizada de Estado de Límite de Velocidad
limit_req_zone $binary_remote_addr zone=api_limit:10m rate=100r/s;
server {
listen 80;
server_name api.example.com;
# Handle rate limit errors
error_page 429 = @rate_limit_error;
location /api {
limit_req zone=api_limit burst=50 nodelay;
limit_req_status 429;
proxy_pass http://backend;
}
location @rate_limit_error {
default_type application/json;
return 429 '{"error": "Too many requests", "retry_after": 60}';
}
}
Limitación de Velocidad de HAProxy
HAProxy usa tablas sticky para limitación de velocidad distribuida:
Limitación de Velocidad Básica
global
stats socket /run/haproxy/admin.sock
defaults
mode http
timeout client 30s
timeout server 30s
frontend api_in
bind *:80
# Stick table for rate limiting
stick-table type ip size 100k expire 1h store http_req_rate(10s)
# Track client IP
http-request track-sc0 src
# Limit to 100 requests per 10 seconds
http-request deny if { sc_http_req_rate(0) gt 100 }
default_backend api_servers
backend api_servers
balance roundrobin
server srv1 192.168.1.100:8000 check
server srv2 192.168.1.101:8000 check
Limitación de Velocidad Por Clave de API
frontend api_in
bind *:80
# Extract API key from header
http-request set-var(req.api_key) req.hdr(X-API-Key)
# Stick table tracking by API key
stick-table type string len 64 size 100k expire 1h store http_req_rate(10s)
http-request track-sc0 var(req.api_key)
# Enforce limit
http-request deny if { sc_http_req_rate(0) gt 1000 }
default_backend api_servers
Limitación de Velocidad Escalonada
frontend api_in
bind *:80
stick-table type string len 64 size 100k expire 1h store http_req_rate(10s)
http-request track-sc0 req.hdr(X-API-Key)
# Different limits based on user tier
acl is_premium_tier req.hdr(X-Tier) -i "premium"
acl is_standard_tier req.hdr(X-Tier) -i "standard"
http-request deny if is_premium_tier !{ sc_http_req_rate(0) lt 5000 }
http-request deny if is_standard_tier !{ sc_http_req_rate(0) lt 500 }
http-request deny if !is_premium_tier !is_standard_tier !{ sc_http_req_rate(0) lt 50 }
default_backend api_servers
Limitación de Velocidad con Backoff Exponencial
frontend api_in
bind *:80
stick-table type ip size 100k expire 1h store http_req_rate(10s), gpc0
http-request track-sc0 src
# First tier: 100 req/s
http-request deny if { sc_http_req_rate(0) gt 100 }
# Mark repeat offenders
http-request set-var(proc.offender) sc0_gpc0 if { sc_http_req_rate(0) gt 100 }
http-request sc-inc-gpc0(0) if { sc_http_req_rate(0) gt 100 }
# Higher penalty for repeat offenders
http-request set-header X-Client-Warning "Rate-limited" if { sc0_gpc0 gt 3 }
http-request deny if { sc0_gpc0 gt 10 }
default_backend api_servers
Análisis de Encabezado de Solicitud
Analice encabezados de solicitud para limitación de velocidad inteligente:
# Rate limit based on User-Agent
map $http_user_agent $is_bot {
default 0;
~*bot 1;
~*crawler 1;
~*spider 1;
}
limit_req_zone $binary_remote_addr zone=normal:10m rate=100r/s;
limit_req_zone $binary_remote_addr zone=bot:10m rate=10r/s;
server {
listen 80;
location /api {
if ($is_bot = 1) {
limit_req zone=bot burst=5;
}
if ($is_bot = 0) {
limit_req zone=normal burst=50;
}
proxy_pass http://backend;
}
}
Limitar por nombre de host:
limit_req_zone $host zone=per_host:10m rate=100r/s;
server {
listen 80;
server_name api.example.com other.example.com;
location / {
limit_req zone=per_host burst=50;
proxy_pass http://backend;
}
}
Limitar por ruta de solicitud:
map $request_uri $request_limit_zone {
~*/api/expensive strict_limit;
~*/api/standard normal_limit;
default no_limit;
}
limit_req_zone $request_limit_zone zone=strict_limit:10m rate=10r/s;
limit_req_zone $request_limit_zone zone=normal_limit:10m rate=100r/s;
server {
listen 80;
location / {
limit_req zone=$request_limit_zone;
proxy_pass http://backend;
}
}
Límites de Velocidad Por Cliente
Implemente límites por cliente sofisticados:
# Combine multiple factors
map "$binary_remote_addr:$http_x_user_id:$http_x_api_key" $rate_limit_key {
default $binary_remote_addr;
~*^(.+):([^:]+):(.+)$ $3; # Prefer API key
~*^(.+):([^:]+)$ $2; # Then user ID
}
limit_req_zone $rate_limit_key zone=client_limit:10m rate=500r/s;
server {
listen 80;
location /api {
limit_req zone=client_limit burst=100 nodelay;
proxy_pass http://backend;
# Pass identified client to backend
proxy_set_header X-Client-ID $rate_limit_key;
}
}
Encabezados de Respuesta de Límite de Velocidad
Incluya encabezados estándar de límite de velocidad en respuestas:
# Add rate limit headers to responses
add_header X-RateLimit-Limit 100 always;
add_header X-RateLimit-Remaining $limit_req_status always;
add_header X-RateLimit-Reset $msec always;
# Custom response when rate limited
error_page 429 = @rate_limited;
location @rate_limited {
default_type application/json;
add_header X-RateLimit-Limit 100 always;
add_header X-RateLimit-Remaining 0 always;
add_header X-RateLimit-Reset 60 always;
add_header Retry-After 60 always;
return 429 '{
"error": "Too Many Requests",
"message": "Rate limit exceeded. Retry after 60 seconds.",
"status": 429
}';
}
Encabezados de límite de velocidad de HAProxy:
http-response set-header X-RateLimit-Limit "1000"
http-response set-header X-RateLimit-Remaining "999"
http-response set-header X-RateLimit-Reset "%T"
# When rate limited
http-response set-header X-RateLimit-Limit "100" if rate_limited
http-response set-header X-RateLimit-Remaining "0" if rate_limited
http-response set-header Retry-After "60" if rate_limited
Limitación de Velocidad Distribuida
Comparta estado de límite de velocidad a través de múltiples servidores usando Redis:
import redis
import time
from flask import Flask, request, jsonify
app = Flask(__name__)
r = redis.Redis(host='localhost', port=6379, db=0)
def check_rate_limit(client_id, limit=100, window=60):
key = f"rate_limit:{client_id}"
# Increment counter
current = r.incr(key)
# Set expiration on first request
if current == 1:
r.expire(key, window)
# Check if exceeded
if current > limit:
return False, current - limit
return True, limit - current
@app.route('/api/resource')
def get_resource():
client_id = request.remote_addr
allowed, remaining = check_rate_limit(client_id)
if not allowed:
return jsonify({'error': 'Rate limit exceeded'}), 429
response = {'data': 'resource data'}
response.headers['X-RateLimit-Remaining'] = str(remaining)
return response
Pruebas y Monitoreo
Pruebe limitación de velocidad:
# Simple rate limit test
for i in {1..150}; do
curl -s -o /dev/null -w "%{http_code} " http://api.example.com/resource
echo -n "$i "
done
echo
# Measure response time under load
ab -n 1000 -c 100 http://api.example.com/api/resource
# Check rate limit headers
curl -i http://api.example.com/api/resource | grep X-RateLimit
# Test with custom headers
for i in {1..5}; do
curl -s -H "X-API-Key: mykey" http://api.example.com/api/resource | head -1
done
Monitoree limitación de velocidad:
# Check Nginx rate limit stats
grep "limiting requests" /var/log/nginx/error.log | wc -l
# Monitor HAProxy stick table
echo "show table api_limit" | socat - /run/haproxy/admin.sock
# Monitor Redis rate limits
redis-cli --scan --pattern "rate_limit:*"
redis-cli DBSIZE
Conclusión
La limitación de velocidad es esencial para protección de API y asignación justa de recursos. Elija algoritmos basados en requisitos: token bucket para tráfico de ráfaga, ventana deslizante para límites estrictos, leaky bucket para rendimiento suave. Implemente estrategias por cliente, por endpoint y escalonadas con encabezados de respuesta claros. Los sistemas distribuidos se benefician de limitación de velocidad respaldada por Redis para consistencia. Monitoree y ajuste límites continuamente basado en patrones de uso real y capacidad del sistema.


