La arquitectura de microservicios requiere una comunicación confiable entre servicios, protección de las solicitudes de API externas y balanceo de carga. Los servidores proxy abordan estas tareas, actuando como intermediarios entre servicios, API externas y clientes. En esta guía, analizaremos cómo integrar correctamente un proxy en la infraestructura de microservicios, qué tipos de proxy utilizar para diferentes escenarios y cómo configurar una comunicación segura.
El papel del proxy en la arquitectura de microservicios
En la arquitectura de microservicios, los servidores proxy cumplen varias funciones críticas que difieren del uso tradicional de proxy para anonimización o eludir bloqueos. Aquí, los proxies se convierten en una parte integral de la infraestructura, asegurando una comunicación confiable y segura entre los componentes del sistema.
Funciones principales del proxy en microservicios:
- API Gateway — un único punto de entrada para todas las solicitudes de los clientes, que las enruta a los microservicios correspondientes, ocultando la arquitectura interna del sistema
- Sidecar Proxy — un contenedor proxy que opera junto a cada servicio (patrón Service Mesh), interceptando todo el tráfico entrante y saliente
- Reverse Proxy — distribución de carga entre múltiples instancias de un servicio, asegurando tolerancia a fallos
- Forward Proxy — control y protección de solicitudes salientes a API externas, ocultando las direcciones IP internas de la infraestructura
- Proxy de seguridad — terminación de SSL/TLS, autenticación, autorización, protección contra DDoS y otros ataques
Los proxies permiten implementar patrones arquitectónicos importantes: circuit breaker (desconexión automática de servicios no funcionales), retry logic (reintentos en caso de fallos), rate limiting (limitación de la frecuencia de solicitudes), request/response transformation (transformación de formatos de datos). Todo esto hace que el sistema sea más resistente a fallos y simplifica la gestión de una infraestructura distribuida compleja.
Importante: En la arquitectura de microservicios, los proxies operan en dos niveles: como puerta de enlace externa para los clientes (API Gateway) y como proxies internos entre servicios (Service Mesh). Ambos niveles son críticos para la seguridad y confiabilidad del sistema.
Tipos de proxy para diferentes escenarios de uso
La elección del tipo de proxy depende de la tarea específica en la arquitectura de microservicios. Diferentes escenarios requieren diferentes características: velocidad, confiabilidad, anonimato o distribución geográfica.
| Escenario | Tipo de proxy | Por qué |
|---|---|---|
| Comunicación interna entre servicios | Proxy HTTP/HTTPS (Envoy, NGINX) | Máxima velocidad, baja latencia, soporte para HTTP/2 |
| Solicitudes a API externas con límites | Proxies residenciales | Eludir límites de tasa, IP reales de usuarios, bajo riesgo de bloqueos |
| Raspado de datos para análisis | Proxies de centros de datos | Alta velocidad, bajo costo, adecuado para solicitudes masivas |
| Trabajo con API móviles | Proxies móviles | Simulación de usuarios móviles reales, acceso a API solo móviles |
| Balanceo de carga | Reverse Proxy (HAProxy, NGINX) | Distribución de tráfico, verificaciones de salud, conmutación automática en caso de fallos |
| Sistema geográficamente distribuido | Proxies residenciales con geo-targeting | Acceso a API regionales, cumplimiento de requisitos de localización de datos |
Para la comunicación interna entre microservicios, generalmente se utilizan soluciones proxy especializadas como Envoy Proxy o NGINX, que están optimizadas para baja latencia y alta capacidad. Soportan protocolos modernos (HTTP/2, gRPC) e integran con sistemas Service Mesh.
Para trabajar con API externas, la elección depende de los requisitos del servicio específico. Si la API tiene límites de tasa estrictos o bloquea solicitudes desde IP de centros de datos, se necesitan proxies residenciales. Para la recopilación masiva de datos, donde la velocidad es más importante que el anonimato, se pueden utilizar proxies de centros de datos. Los proxies móviles son necesarios al trabajar con API que verifican el tipo de dispositivo o requieren direcciones IP móviles.
Proxy como API Gateway: protección y enrutamiento
API Gateway es un servidor proxy especializado que actúa como un único punto de entrada para todas las solicitudes de clientes al sistema de microservicios. En lugar de que los clientes se dirijan directamente a decenas de diferentes servicios, envían todas las solicitudes a una única dirección API Gateway, que las enruta a los servicios necesarios.
Funciones principales de API Gateway:
- Enrutamiento de solicitudes — determinación de qué microservicio debe procesar la solicitud, basado en la URL, encabezados u otros parámetros
- Autenticación y autorización — verificación de tokens (JWT, OAuth), gestión de acceso a diferentes servicios
- Limitación de tasa — restricción del número de solicitudes de un solo cliente para proteger contra sobrecargas y DDoS
- Agregación de respuestas — combinación de datos de varios servicios en una sola respuesta al cliente
- Transformación de protocolos — conversión de REST a gRPC, HTTP/1.1 a HTTP/2
- Caching — almacenamiento de datos solicitados con frecuencia para reducir la carga en los servicios
- Registro y monitoreo — recopilación centralizada de métricas y registros de todas las solicitudes
Soluciones populares para API Gateway: Kong, Tyk, AWS API Gateway, Azure API Management, NGINX Plus, Traefik. La elección depende de la escala del sistema, los requisitos de rendimiento y la plataforma en la nube utilizada.
// Ejemplo de configuración de NGINX como API Gateway
upstream auth_service {
server auth:8001;
}
upstream user_service {
server user:8002;
}
upstream order_service {
server order:8003;
}
server {
listen 80;
server_name api.example.com;
# Limitación de frecuencia de solicitudes
limit_req_zone $binary_remote_addr zone=api_limit:10m rate=10r/s;
location /api/auth/ {
limit_req zone=api_limit burst=20;
proxy_pass http://auth_service/;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
location /api/users/ {
# Verificación de token antes de proxy
auth_request /auth/verify;
proxy_pass http://user_service/;
}
location /api/orders/ {
auth_request /auth/verify;
proxy_pass http://order_service/;
}
# Endpoint interno para verificación de tokens
location = /auth/verify {
internal;
proxy_pass http://auth_service/verify;
proxy_pass_request_body off;
proxy_set_header Content-Length "";
}
}
API Gateway oculta la arquitectura interna del sistema de los clientes externos. Los clientes no saben cuántos microservicios existen y cómo interactúan; solo ven una API única. Esto simplifica el versionado, permite cambiar la estructura interna sin afectar a los clientes y mejora la seguridad, ya que los servicios internos no son accesibles directamente desde Internet.
Integración con Service Mesh (Istio, Linkerd)
Service Mesh es una capa de infraestructura que gestiona la comunicación entre microservicios mediante servidores proxy desplegados junto a cada servicio (patrón Sidecar). A diferencia de API Gateway, que solo maneja tráfico externo, Service Mesh controla todo el tráfico interno entre servicios.
Las soluciones más populares de Service Mesh son Istio (que utiliza Envoy Proxy como sidecar) y Linkerd (que utiliza su propio proxy ligero). Automáticamente inyectan un contenedor proxy junto a cada pod en Kubernetes, interceptando todo el tráfico entrante y saliente.
Capacidades de Service Mesh a través de proxies:
- Mutual TLS (mTLS) — cifrado automático de todo el tráfico entre servicios con autenticación mutua
- Gestión de tráfico — control de enrutamiento, despliegues canarios, pruebas A/B
- Observabilidad — recopilación automática de métricas, trazas y registros sin modificar el código de los servicios
- Resiliencia — circuit breaking, lógica de reintentos, gestión de timeouts, inyección de fallos para pruebas
- Descubrimiento de servicios — descubrimiento automático de servicios y balanceo de carga
# Ejemplo de configuración de Istio VirtualService para enrutamiento
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
name: user-service
spec:
hosts:
- user-service
http:
- match:
- headers:
version:
exact: "v2"
route:
- destination:
host: user-service
subset: v2
- route:
- destination:
host: user-service
subset: v1
weight: 90
- destination:
host: user-service
subset: v2
weight: 10 # Despliegue canario: 10% del tráfico a v2
---
# Circuit Breaker para protección contra fallos en cascada
apiVersion: networking.istio.io/v1beta1
kind: DestinationRule
metadata:
name: user-service
spec:
host: user-service
trafficPolicy:
connectionPool:
tcp:
maxConnections: 100
http:
http1MaxPendingRequests: 50
maxRequestsPerConnection: 2
outlierDetection:
consecutiveErrors: 5
interval: 30s
baseEjectionTime: 30s
maxEjectionPercent: 50
Service Mesh resuelve el problema del "monolito distribuido" — cuando la lógica de interacción entre servicios (reintentos, timeouts, circuit breaking) se duplica en el código de cada servicio. En su lugar, toda esta lógica se traslada a la capa de proxy, lo que simplifica el código de los servicios y asegura un comportamiento uniforme en todo el sistema.
Una ventaja importante es la transparencia total del tráfico. Cada solicitud entre servicios pasa a través de un proxy que registra métricas: tiempo de respuesta, códigos de error, tamaño del payload. Estos datos se envían automáticamente a sistemas de monitoreo (Prometheus, Grafana) y trazado (Jaeger, Zipkin), creando una imagen completa del funcionamiento del sistema distribuido sin necesidad de añadir instrumentación en el código de cada servicio.
Protección de solicitudes a API externas a través de proxy
Los microservicios a menudo interactúan con API externas: sistemas de pago, servicios de geolocalización, API de redes sociales, proveedores de datos. Las solicitudes directas a API externas crean varios problemas: divulgación de direcciones IP internas de la infraestructura, riesgo de bloqueo al superar los límites de tasa, falta de control sobre el tráfico saliente.
El uso de proxies para solicitudes salientes resuelve estos problemas y agrega capacidades adicionales:
- Ocultamiento de infraestructura — las API externas ven las direcciones IP de los proxies, no las de sus servidores
- Eludir límites de tasa — rotación de direcciones IP para distribuir solicitudes
- Distribución geográfica — acceso a API regionales a través de proxies en los países necesarios
- Gestión centralizada — un único punto de control para todas las solicitudes salientes
- Caching de respuestas — reducción del número de solicitudes a API costosas
- Monitoreo y registro — seguimiento de todas las interacciones con servicios externos
// Python: configuración de proxy para requests a API externas
import requests
from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry
class ExternalAPIClient:
def __init__(self, proxy_url, proxy_rotation=False):
self.session = requests.Session()
# Configuración del proxy
self.proxies = {
'http': proxy_url,
'https': proxy_url
}
# Lógica de reintentos para resiliencia
retry_strategy = Retry(
total=3,
backoff_factor=1,
status_forcelist=[429, 500, 502, 503, 504],
allowed_methods=["GET", "POST"]
)
adapter = HTTPAdapter(max_retries=retry_strategy)
self.session.mount("http://", adapter)
self.session.mount("https://", adapter)
def call_payment_api(self, data):
"""Solicitud a la API de pagos a través del proxy"""
try:
response = self.session.post(
'https://api.payment-provider.com/charge',
json=data,
proxies=self.proxies,
timeout=10,
headers={'User-Agent': 'MyService/1.0'}
)
response.raise_for_status()
return response.json()
except requests.exceptions.RequestException as e:
# Registro del error
print(f"Error en la API de pagos: {e}")
raise
# Uso con un grupo de proxies para rotación
class ProxyPool:
def __init__(self, proxy_list):
self.proxies = proxy_list
self.current = 0
def get_next(self):
proxy = self.proxies[self.current]
self.current = (self.current + 1) % len(self.proxies)
return proxy
# Inicialización
proxy_pool = ProxyPool([
'http://user:pass@proxy1.example.com:8080',
'http://user:pass@proxy2.example.com:8080',
'http://user:pass@proxy3.example.com:8080'
])
# Para cada solicitud usamos el siguiente proxy
client = ExternalAPIClient(proxy_pool.get_next())
Para trabajar con API externas que tienen restricciones estrictas o bloquean solicitudes desde IP de centros de datos, los proxies residenciales se vuelven una necesidad. Proporcionan direcciones IP reales de usuarios domésticos, lo que reduce el riesgo de bloqueo y permite eludir restricciones geográficas.
// Node.js: proxy para API externas con rotación automática
const axios = require('axios');
const HttpsProxyAgent = require('https-proxy-agent');
class ExternalAPIService {
constructor(proxyList) {
this.proxyList = proxyList;
this.currentProxyIndex = 0;
this.requestCounts = new Map(); // Contador de solicitudes para limitación de tasa
}
getNextProxy() {
const proxy = this.proxyList[this.currentProxyIndex];
this.currentProxyIndex = (this.currentProxyIndex + 1) % this.proxyList.length;
return proxy;
}
async callAPI(endpoint, data, options = {}) {
const proxyUrl = this.getNextProxy();
const agent = new HttpsProxyAgent(proxyUrl);
// Limitación de tasa: no más de 100 solicitudes por minuto en el proxy
const proxyKey = proxyUrl;
const now = Date.now();
const count = this.requestCounts.get(proxyKey) || { count: 0, resetTime: now + 60000 };
if (count.count >= 100 && now < count.resetTime) {
// Cambiamos al siguiente proxy
return this.callAPI(endpoint, data, options);
}
try {
const response = await axios({
method: options.method || 'POST',
url: endpoint,
data: data,
httpsAgent: agent,
timeout: options.timeout || 10000,
headers: {
'User-Agent': 'Mozilla/5.0 (compatible; MyService/1.0)',
...options.headers
}
});
// Actualizamos el contador
if (now >= count.resetTime) {
this.requestCounts.set(proxyKey, { count: 1, resetTime: now + 60000 });
} else {
count.count++;
}
return response.data;
} catch (error) {
if (error.response?.status === 429) {
// Límite de tasa excedido - cambiamos a otro proxy
console.log(`Límite de tasa en ${proxyUrl}, cambiando proxy`);
return this.callAPI(endpoint, data, options);
}
throw error;
}
}
}
// Uso
const apiService = new ExternalAPIService([
'http://user:pass@proxy1.example.com:8080',
'http://user:pass@proxy2.example.com:8080'
]);
module.exports = apiService;
Balanceo de carga y tolerancia a fallos
Los servidores proxy juegan un papel clave en asegurar la alta disponibilidad del sistema de microservicios a través del balanceo de carga y la conmutación automática en caso de fallos. Cuando se ejecutan múltiples instancias de un mismo servicio (para escalado horizontal), el proxy distribuye las solicitudes entre ellas, asegurando una carga uniforme.
Algoritmos principales de balanceo de carga:
- Round Robin — envío secuencial de solicitudes a cada servidor en la lista, simple y efectivo para servidores homogéneos
- Least Connections — envío de solicitudes al servidor con menos conexiones activas, adecuado para solicitudes largas
- IP Hash — vinculación del cliente a un servidor específico basado en su IP, asegura sesiones pegajosas
- Weighted Round Robin — distribución considerando la capacidad de los servidores (los más potentes reciben más solicitudes)
- Random — selección aleatoria de servidor, adecuado para servicios sin estado
# Configuración de HAProxy para balanceo con verificaciones de salud
global
maxconn 4096
log stdout format raw local0
defaults
mode http
timeout connect 5s
timeout client 50s
timeout server 50s
option httplog
frontend api_frontend
bind *:80
default_backend api_servers
backend api_servers
balance roundrobin
# Verificación de salud: comprobación de /health cada 2 segundos
option httpchk GET /health
http-check expect status 200
# Lógica de reintentos
retries 3
option redispatch
# Servidores con pesos (server3 es 2 veces más potente)
server server1 10.0.1.10:8080 check weight 1 maxconn 500
server server2 10.0.1.11:8080 check weight 1 maxconn 500
server server3 10.0.1.12:8080 check weight 2 maxconn 1000
# Servidor de respaldo (se utiliza solo si los principales no están disponibles)
server backup1 10.0.2.10:8080 check backup
Las verificaciones de salud son una función crítica para la tolerancia a fallos. El proxy verifica regularmente la disponibilidad de cada servidor (generalmente a través del endpoint HTTP /health o /ready) y excluye automáticamente los servidores no funcionales del grupo de balanceo. Cuando un servidor se recupera y comienza a responder a las verificaciones de salud, se reincorpora automáticamente al grupo.
Estrategias de tolerancia a fallos a través de proxies:
- Verificaciones de salud activas — el proxy interroga activamente a los servidores para verificar su disponibilidad
- Verificaciones de salud pasivas — el proxy rastrea solicitudes reales y excluye servidores al acumular errores
- Circuit Breaker — desconexión temporal de un servicio problemático para evitar fallos en cascada
- Degradación elegante — cambio a un modo de operación simplificado o datos en caché en caso de fallos
- Failover a respaldo — conmutación automática a servidores o regiones de respaldo
// Python: implementación de Circuit Breaker para proxy a servicios externos
from datetime import datetime, timedelta
from enum import Enum
class CircuitState(Enum):
CLOSED = "closed" # Operación normal
OPEN = "open" # Servicio no disponible, solicitudes bloqueadas
HALF_OPEN = "half_open" # Modo de prueba después de la recuperación
class CircuitBreaker:
def __init__(self, failure_threshold=5, timeout=60, success_threshold=2):
self.failure_threshold = failure_threshold # Errores antes de abrir
self.timeout = timeout # Segundos antes de intentar la recuperación
self.success_threshold = success_threshold # Éxitos para cerrar
self.state = CircuitState.CLOSED
self.failures = 0
self.successes = 0
self.last_failure_time = None
def call(self, func, *args, **kwargs):
if self.state == CircuitState.OPEN:
if datetime.now() - self.last_failure_time > timedelta(seconds=self.timeout):
self.state = CircuitState.HALF_OPEN
print("Circuit breaker: transición a HALF_OPEN")
else:
raise Exception("Circuit breaker OPEN: servicio no disponible")
try:
result = func(*args, **kwargs)
self._on_success()
return result
except Exception as e:
self._on_failure()
raise e
def _on_success(self):
self.failures = 0
if self.state == CircuitState.HALF_OPEN:
self.successes += 1
if self.successes >= self.success_threshold:
self.state = CircuitState.CLOSED
self.successes = 0
print("Circuit breaker: recuperación, transición a CLOSED")
def _on_failure(self):
self.failures += 1
self.last_failure_time = datetime.now()
if self.failures >= self.failure_threshold:
self.state = CircuitState.OPEN
print(f"Circuit breaker OPEN: {self.failures} errores consecutivos")
# Uso
breaker = CircuitBreaker(failure_threshold=3, timeout=30)
def call_external_service():
# Su código para solicitar a la API externa a través del proxy
pass
try:
result = breaker.call(call_external_service)
except Exception as e:
# Lógica de fallback: caché, valores predeterminados, etc.
print(f"Servicio no disponible: {e}")
Seguridad en la comunicación entre servicios
En la arquitectura de microservicios, los servidores proxy proporcionan varios niveles de seguridad: cifrado de tráfico, autenticación de servicios, protección contra ataques y aislamiento de segmentos de red. Sin una configuración de seguridad adecuada, el tráfico interno entre servicios puede ser interceptado o falsificado.
Aspectos clave de la seguridad a través de proxies:
- Mutual TLS (mTLS) — autenticación bidireccional, donde tanto el cliente como el servidor verifican los certificados entre sí. Service Mesh configura automáticamente mTLS entre todos los servicios
- Terminación de TLS — el proxy descifra el tráfico HTTPS en el borde, lo verifica y lo transmite a los servicios a través de un canal seguro
- Validación de JWT — verificación de tokens de acceso a nivel de proxy, antes de que la solicitud llegue al servicio
- IP Whitelisting — restricción de acceso a servicios solo desde direcciones IP permitidas
- Protección contra DDoS — limitación de tasa, límites de conexión, protección contra SYN flood a nivel de proxy
- WAF (Web Application Firewall) — filtrado de solicitudes maliciosas, protección contra inyección SQL, XSS
# Configuración de NGINX con SSL/TLS y seguridad
server {
listen 443 ssl http2;
server_name api.internal.example.com;
# Certificados SSL
ssl_certificate /etc/nginx/certs/api.crt;
ssl_certificate_key /etc/nginx/certs/api.key;
# Protocolos y cifrados modernos
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
ssl_prefer_server_ciphers on;
# Certificado del cliente para mTLS
ssl_client_certificate /etc/nginx/certs/ca.crt;
ssl_verify_client on;
# Encabezados seguros
add_header Strict-Transport-Security "max-age=31536000" always;
add_header X-Frame-Options "DENY" always;
add_header X-Content-Type-Options "nosniff" always;
# Limitación de tasa
limit_req_zone $binary_remote_addr zone=api:10m rate=100r/s;
limit_req zone=api burst=200 nodelay;
# Limitación de conexiones
limit_conn_zone $binary_remote_addr zone=addr:10m;
limit_conn addr 10;
# IP whitelisting
allow 10.0.0.0/8; # Red interna
allow 172.16.0.0/12; # VPC
deny all;
location / {
# Verificación de token JWT
auth_jwt "API restringida";
auth_jwt_key_file /etc/nginx/jwt_key.json;
proxy_pass http://backend_service;
# Transmisión de información sobre el certificado del cliente
proxy_set_header X-Client-DN $ssl_client_s_dn;
proxy_set_header X-Client-Verify $ssl_client_verify;
}
}
Service Mesh simplifica enormemente la configuración de seguridad, generando y rotando automáticamente certificados para mTLS, aplicando políticas de acceso y cifrando todo el tráfico entre servicios. Por ejemplo, en Istio se puede establecer una política que el servicio "payment" solo puede recibir solicitudes del servicio "order", y esto se aplicará automáticamente a nivel de proxy sin modificar el código de los servicios.
Importante para producción: Siempre use mTLS para la comunicación interna entre servicios, incluso si están en una misma red privada. Esto protege contra ataques de tipo man-in-the-middle y asegura la autenticación a nivel de servicios, no solo a nivel de red.
Monitoreo y registro del tráfico proxy
Los servidores proxy ofrecen una oportunidad única para el monitoreo centralizado de todo el tráfico en el sistema de microservicios. Dado que todo el tráfico pasa a través del proxy (tanto externo a través de API Gateway como interno a través de Service Mesh), se obtiene una visibilidad completa del funcionamiento del sistema sin necesidad de instrumentar cada servicio.
Métricas clave para monitoreo a nivel de proxy:
- Latencia — tiempo de procesamiento de la solicitud en cada etapa: proxy, servicio, API externas
- Throughput — número de solicitudes por segundo, volumen de datos transferidos
- Tasa de errores — porcentaje de errores (4xx, 5xx), tipos de errores, endpoints problemáticos
- Métricas de conexión — número de conexiones activas, uso del pool de conexiones
- Estado del Circuit Breaker — estado de los circuit breakers para cada servicio
- Métricas de SSL/TLS — estado de los certificados, versiones de protocolos, errores de handshake
# Configuración de NGINX para exportar métricas a Prometheus
server {
listen 9113;
location /metrics {
stub_status;
access_log off;
allow 10.0.0.0/8; # Solo para el servidor Prometheus
deny all;
}
}
# Registro en formato JSON para logging estructurado
log_format json_combined escape=json
'{'
'"time_local":"$time_local",'
'"remote_addr":"$remote_addr",'
'"request":"$request",'
'"status": "$status",'
'"body_bytes_sent":"$body_bytes_sent",'
'"request_time":"$request_time",'
'"upstream_response_time":"$upstream_response_time",'
'"upstream_addr":"$upstream_addr",'
'"http_referrer":"$http_referer",'
'"http_user_agent":"$http_user_agent",'
'"http_x_forwarded_for":"$http_x_forwarded_for"'
'}';
access_log /var/log/nginx/access.log json_combined;
El trazado distribuido es una de las capacidades más poderosas de monitoreo a través de proxies. Cada solicitud recibe un ID de traza único, que el proxy agrega a los encabezados y transmite a lo largo de la cadena de servicios. Los sistemas de trazado (Jaeger, Zipkin) recopilan información de todos los proxies y construyen el camino completo de la solicitud a través del sistema, mostrando cuánto tiempo pasó en cada servicio.
// Node.js: agregar trazado en el middleware proxy
const express = require('express');
const { v4: uuidv4 } = require('uuid');
const axios = require('axios');
const app = express();
// Middleware para agregar ID de traza
app.use((req, res, next) => {
// Obtener ID de traza del encabezado o crear uno nuevo
const traceId = req.headers['x-trace-id'] || uuidv4();
const spanId = uuidv4();
// Agregar a los encabezados para pasar más adelante
req.traceId = traceId;
req.spanId = spanId;
res.setHeader('x-trace-id', traceId);
// Registrar el inicio del procesamiento
const startTime = Date.now();
res.on('finish', () => {
const duration = Date.now() - startTime;
// Registro estructurado para análisis
console.log(JSON.stringify({
timestamp: new Date().toISOString(),
traceId: traceId,
spanId: spanId,
method: req.method,
path: req.path,
status: res.statusCode,
duration: duration,
userAgent: req.headers['user-agent'],
ip: req.ip
}));
});
next();
});
// Endpoint proxy con transmisión de encabezados de trazado
app.all('/api/*', async (req, res) => {
const targetService = determineTargetService(req.path);
try {
const response = await axios({
method: req.method,
url: `http://${targetService}${req.path}`,
data: req.body,
headers: {
...req.headers,
'x-trace-id': req.traceId,
'x-parent-span-id': req.spanId,
'x-span-id': uuidv4() // Nuevo span para la solicitud downstream
}
});
res.status(response.status).json(response.data);
} catch (error) {
console.error(JSON.stringify({
traceId: req.traceId,
error: error.message,
service: targetService
}));
res.status(500).json({ error: 'Servicio no disponible' });
}
});
function determineTargetService(path) {
if (path.startsWith('/api/users')) return 'user-service:8080';
if (path.startsWith('/api/orders')) return 'order-service:8080';
return 'default-service:8080';
}
app.listen(3000);
Las alertas basadas en métricas de proxy permiten detectar problemas rápidamente. Por ejemplo, se pueden configurar alertas para: un aumento repentino en la latencia (posiblemente uno de los servicios está degradándose), un aumento en la tasa de errores por encima de un umbral (problemas con el código o dependencias), cambios en los patrones de tráfico (posible ataque DDoS o carga viral).
Ejemplos de implementación en Python y Node.js
Consideremos ejemplos prácticos de integración de proxies en microservicios en Python y Node.js para diferentes escenarios: comunicación interna, trabajo con API externas, balanceo de carga.
Python: servicio con proxy para API externas
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
import httpx
import asyncio
from typing import List, Optional
import logging
app = FastAPI()
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
class ProxyConfig(BaseModel):
url: str
max_requests_per_minute: int = 60
class ProxyPool:
def __init__(self, proxies: List[ProxyConfig]):
self.proxies = proxies
self.current_index = 0
self.request_counts = {p.url: 0 for p in proxies}
self.reset_time = asyncio.get_event_loop().time() + 60
async def get_next_proxy(self) -> str:
# Reiniciar contadores cada minuto
current_time = asyncio.get_event_loop().time()
if current_time >= self.reset_time:
self.request_counts = {p.url: 0 for p in self.proxies}
self.reset_time = current_time + 60
# Encontrar un proxy con solicitudes disponibles
for _ in range(len(self.proxies)):
proxy = self.proxies[self.current_index]
self.current_index = (self.current_index + 1) % len(self.proxies)
if self.request_counts[proxy.url] < proxy.max_requests_per_minute:
self.request_counts[proxy.url] += 1
return proxy.url
# Todos los proxies han agotado el límite
raise HTTPException(status_code=429, detail="Todos los proxies limitados por tasa")
# Inicialización del grupo de proxies
proxy_pool = ProxyPool([
ProxyConfig(url="http://user:pass@proxy1.example.com:8080", max_requests_per_minute=100),
ProxyConfig(url="http://user:pass@proxy2.example.com:8080", max_requests_per_minute=100),
ProxyConfig(url="http://user:pass@proxy3.example.com:8080", max_requests_per_minute=100)
])
class ExternalAPIClient:
def __init__(self, proxy_pool: ProxyPool):
self.proxy_pool = proxy_pool
async def fetch_data(self, endpoint: str, params: dict = None) -> dict:
proxy_url = await self.proxy_pool.get_next_proxy()
async with httpx.AsyncClient(proxies={"http://": proxy_url, "https://": proxy_url}) as client:
try:
response = await client.get(
endpoint,
params=params,
timeout=10.0,
headers={"User-Agent": "MyMicroservice/1.0"}
)
response.raise_for_status()
logger.info(f"Datos obtenidos con éxito de {endpoint} a través de {proxy_url}")
return response.json()
except httpx.HTTPStatusError as e:
logger.error(f"Error HTTP {e.response.status_code} de {endpoint}")
raise HTTPException(status_code=e.response.status_code, detail=str(e))
except httpx.RequestError as e:
logger.error(f"Error de solicitud a {endpoint}: {e}")
raise HTTPException(status_code=503, detail="API externa no disponible")
api_client = ExternalAPIClient(proxy_pool)
@app.get("/data/{resource_id}")
async def get_external_data(resource_id: str):
"""Endpoint que obtiene datos de una API externa a través de un proxy"""
external_endpoint = f"https://api.external-service.com/v1/resources/{resource_id}"
try:
data = await api_client.fetch_data(external_endpoint)
return {"status": "success", "data": data}
except HTTPException:
raise
except Exception as e:
logger.error(f"Error inesperado: {e}")
raise HTTPException(status_code=500, detail="Error interno del servidor")
@app.get("/health")
async def health_check():
return {"status": "healthy", "service": "external-api-proxy"}
# Ejecución: uvicorn main:app --host 0.0.0.0 --port 8000
Node.js: API Gateway con balanceo de carga
const express = require('express');
const axios = require('axios');
const rateLimit = require('express-rate-limit');
const app = express();
app.use(express.json());
// Configuración de microservicios
const services = {
users: [
{ url: 'http://user-service-1:8001', healthy: true, activeConnections: 0 },
{ url: 'http://user-service-2:8001', healthy: true, activeConnections: 0 },
// más servicios...
],
orders: [
{ url: 'http://order-service-1:8002', healthy: true, activeConnections: 0 },
{ url: 'http://order-service-2:8002', healthy: true, activeConnections: 0 },
// más servicios...
],
};
// Middleware para balanceo de carga
app.use('/api/users', (req, res, next) => {
const service = getNextService(services.users);
proxyRequest(service.url, req, res);
});
app.use('/api/orders', (req, res, next) => {
const service = getNextService(services.orders);
proxyRequest(service.url, req, res);
});
// Función para obtener el siguiente servicio
function getNextService(serviceList) {
// Lógica para seleccionar el siguiente servicio
return serviceList[0]; // Simplificado para el ejemplo
}
// Función para realizar la solicitud proxy
function proxyRequest(serviceUrl, req, res) {
axios({
method: req.method,
url: `${serviceUrl}${req.originalUrl}`,
data: req.body,
headers: {
...req.headers,
'X-Forwarded-For': req.ip,
},
})
.then(response => {
res.status(response.status).json(response.data);
})
.catch(error => {
res.status(error.response ? error.response.status : 500).json({ error: 'Error en el servicio' });
});
}
// Ejecución del servidor
app.listen(3000, () => {
console.log('API Gateway escuchando en el puerto 3000');
});