Patrons et Stratégies d'Implémentation d'API Gateway
Les API gateways servent de point d'entrée unique pour que les applications cliente accèdent aux microservices, fournissant des préoccupations transversales essentielles comme l'authentification, la limitation de débit, le routage et le monitoring. Comprendre les patrons de gateway est crucial pour construire des systèmes distribués évolutifs et sécurisés.
Architecture Centrale d'API Gateway
Placement de Gateway dans les Microservices
API Gateway comme Point d'Entrée Unique :
Internet ──► API Gateway ──► Service Registry
│ │ │
▼ ▼ ▼
Clients ──────────┼───────────────┼─► Microservice A
│ │
└───────────────┼─► Microservice B
│
└─► Microservice C
Responsabilités du Gateway :
- Routage des Demandes : Diriger les demandes vers les services appropriés
- Authentification : Valider les identifiants et permissions utilisateur
- Limitation de Débit : Contrôler les taux de demande pour prévenir l'abus
- Équilibrage de Charge : Distribuer les demandes entre les instances de service
- Monitoring : Suivre les métriques de demande et santé
Patrons de Routage
Routage Basé sur le Chemin
Mappage de Chemin URL :
/api/v1/users/* ──► User Service
/api/v1/orders/* ──► Order Service
/api/v1/products/* ──► Product Service
Exemple d'Implémentation :
location /api/v1/users/ {
proxy_pass http://user-service:8080/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
location /api/v1/orders/ {
proxy_pass http://order-service:8080/;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
Routage Basé sur l'En-tête
Routage par Content-Type :
Content-Type: application/json ──► JSON Service
Content-Type: application/xml ──► XML Service
Routage Basé sur la Version :
X-API-Version: v1 ──► Legacy Service
X-API-Version: v2 ──► Modern Service
Routage Dynamique avec Découverte de Service
Intégration avec Service Registry :
API Gateway ──► Consul/Eureka ──► Service Instances
│ │
└─► Health Check ──► Load Balance ──► Route Request
Authentification et Autorisation
Validation de Token JWT
Flux JWT :
Client ──► JWT Token ──► Gateway ──► Validate Token ──► Service
▲ │ │
└──────────────────────┴───────────────┘
Token Refresh
Implémentation de Validation de Token :
class JwtAuthMiddleware {
async validateToken(token: string): Promise<UserClaims> {
try {
const decoded = jwt.verify(token, process.env.JWT_SECRET);
return {
userId: decoded.sub,
roles: decoded.roles,
permissions: decoded.permissions
};
} catch (error) {
throw new AuthenticationError('Invalid token');
}
}
async authorizeRequest(claims: UserClaims, requiredRoles: string[]): Promise<boolean> {
return requiredRoles.some(role => claims.roles.includes(role));
}
}
Intégration OAuth2
Flux de Code d'Autorisation :
Client ──► Gateway ──► Auth Server ──► User Consent ──► Tokens
▲ │ │ │
└──────────────┼────────────────────┼────────────────────┘
Redirect with Code ──► Exchange for Tokens
Authentification par Clé API
Accès Basé sur Clé :
class ApiKeyMiddleware {
private keyStore: Map<string, ApiKeyConfig> = new Map();
async validateApiKey(apiKey: string): Promise<boolean> {
const config = this.keyStore.get(apiKey);
if (!config) return false;
// Check rate limits, expiration, etc.
return config.active && !this.isExpired(config);
}
async getRateLimit(apiKey: string): Promise<RateLimit> {
const config = this.keyStore.get(apiKey);
return config?.rateLimit || { requests: 100, period: '1h' };
}
}
Patrons de Limitation de Débit
Algorithme Token Bucket
Implémentation Token Bucket :
class TokenBucket {
private tokens: number;
private lastRefill: number;
private readonly capacity: number;
private readonly refillRate: number; // tokens per second
constructor(capacity: number, refillRate: number) {
this.capacity = capacity;
this.refillRate = refillRate;
this.tokens = capacity;
this.lastRefill = Date.now();
}
allowRequest(): boolean {
this.refill();
if (this.tokens >= 1) {
this.tokens--;
return true;
}
return false;
}
private refill(): void {
const now = Date.now();
const timePassed = (now - this.lastRefill) / 1000;
const tokensToAdd = timePassed * this.refillRate;
this.tokens = Math.min(this.capacity, this.tokens + tokensToAdd);
this.lastRefill = now;
}
}
Compteur de Fenêtre Glissante
Limitation de Débit Distribuée :
class SlidingWindowLimiter {
private redis: Redis;
async isAllowed(key: string, limit: number, windowMs: number): Promise<boolean> {
const now = Date.now();
const windowStart = now - windowMs;
// Remove old entries
await this.redis.zremrangebyscore(key, 0, windowStart);
// Count current requests
const requestCount = await this.redis.zcard(key);
if (requestCount >= limit) {
return false;
}
// Add current request
await this.redis.zadd(key, now, `${now}-${Math.random()}`);
await this.redis.expire(key, Math.ceil(windowMs / 1000));
return true;
}
}
Transformation de Demandes
Transformation de Données
Enrichissement de Demande :
Incoming Request ──► Add User Context ──► Service
│ │
└─► {userId: 123} ───────┘
Transformation de Réponse :
Service Response ──► Remove Sensitive Data ──► Client
│ │
└─► Filter Fields ──────────┘
Traduction de Protocole
REST vers GraphQL :
REST API ──► Gateway ──► GraphQL Schema ──► Resolvers
│ │ │
└──────────────┼──────────┘
Query Optimization
Stratégies de Cache
Cache de Réponse
En-têtes de Cache HTTP :
class CacheMiddleware {
async handleRequest(req: Request, res: Response): Promise<void> {
const cacheKey = this.generateCacheKey(req);
// Check cache
const cached = await this.cache.get(cacheKey);
if (cached && this.isCacheValid(cached, req)) {
res.setHeader('X-Cache', 'HIT');
res.send(cached.data);
return;
}
// Set cache headers for response
res.setHeader('Cache-Control', 'public, max-age=300');
res.setHeader('ETag', this.generateETag(res.body));
// Cache the response
await this.cache.set(cacheKey, {
data: res.body,
headers: res.headers,
timestamp: Date.now()
});
}
}
Cache Distribué
Réplication de Cache :
API Gateway ──► Redis Cluster ──► Cache Hit
│ │
└──────────────┘
Cache Miss → Origin
Patron Circuit Breaker
États du Circuit Breaker
Machine d'États :
Closed ──► Open (when failures > threshold)
▲ │
└─────────┼─► Half-Open (test request)
│
└─► Closed (if success)
Implémentation :
enum CircuitState {
CLOSED = 'CLOSED',
OPEN = 'OPEN',
HALF_OPEN = 'HALF_OPEN'
}
class CircuitBreaker {
private state: CircuitState = CircuitState.CLOSED;
private failureCount = 0;
private lastFailureTime = 0;
async execute<T>(operation: () => Promise<T>): Promise<T> {
if (this.state === CircuitState.OPEN) {
if (this.shouldAttemptReset()) {
this.state = CircuitState.HALF_OPEN;
} else {
throw new CircuitBreakerError('Circuit is open');
}
}
try {
const result = await operation();
this.onSuccess();
return result;
} catch (error) {
this.onFailure();
throw error;
}
}
private onSuccess(): void {
this.failureCount = 0;
this.state = CircuitState.CLOSED;
}
private onFailure(): void {
this.failureCount++;
this.lastFailureTime = Date.now();
if (this.failureCount >= this.failureThreshold) {
this.state = CircuitState.OPEN;
}
}
}
Monitoring et Observabilité
Métriques de Gateway
Métriques Principales à Suivre :
- Latence de demande par endpoint
- Taux d'erreur par service
- Succès de limitation de débit
- Échecs d'authentification
- Taux de succès/échec de cache
Collecte de Métriques :
class MetricsCollector {
private metrics: Map<string, Metric> = new Map();
recordRequest(endpoint: string, method: string, statusCode: number, duration: number): void {
const key = `${method}:${endpoint}`;
this.metrics.set(key, {
count: (this.metrics.get(key)?.count || 0) + 1,
totalDuration: (this.metrics.get(key)?.totalDuration || 0) + duration,
statusCodes: {
...this.metrics.get(key)?.statusCodes,
[statusCode]: (this.metrics.get(key)?.statusCodes?.[statusCode] || 0) + 1
}
});
}
getMetrics(): GatewayMetrics {
return {
endpoints: Array.from(this.metrics.entries()).map(([key, metric]) => ({
endpoint: key,
requests: metric.count,
avgLatency: metric.totalDuration / metric.count,
statusCodes: metric.statusCodes
}))
};
}
}
Traçage Distribué
Traçage de Demande :
Client Request ──► Gateway (span-1) ──► Service A (span-2) ──► Service B (span-3)
│ │ │ │
└─ Trace ID: abc-123 ──┼─ Parent: span-1 ─────┼─ Parent: span-2 ─────┘
Orchestration de Service
Composition d'API
Agrégation de Demandes :
Gateway ──► Parallel Requests ──► Combine Results
│ ├─► Service A
│ ├─► Service B
│ └─► Service C
│
└─► Single Response
Orchestration Saga
Coordination de Transaction Distribuée :
Gateway ──► Start Saga ──► Service A ──► Service B ──► Complete
│ │ │ │
└─────────┼──────────────┼──────────────┘
Compensate on Failure
Meilleures Pratiques de Sécurité
Validation et Sanitisation d'Entrée
Validation de Demande :
class ValidationMiddleware {
async validateRequest(req: Request): Promise<ValidationResult> {
const schema = this.getSchema(req.path);
try {
const validated = await schema.validate(req.body);
return { valid: true, data: validated };
} catch (error) {
return {
valid: false,
errors: error.errors.map(err => ({
field: err.field,
message: err.message
}))
};
}
}
}
Configuration CORS
Cross-Origin Resource Sharing :
const corsOptions = {
origin: (origin, callback) => {
const allowedOrigins = process.env.ALLOWED_ORIGINS?.split(',') || [];
if (allowedOrigins.includes(origin) || !origin) {
callback(null, true);
} else {
callback(new Error('Not allowed by CORS'));
}
},
credentials: true,
methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'],
allowedHeaders: ['Content-Type', 'Authorization', 'X-API-Key']
};
Optimisation des Performances
Pooling de Connexions
Configuration de Client HTTP :
const httpClient = axios.create({
baseURL: 'http://service-registry',
timeout: 5000,
httpAgent: new http.Agent({
keepAlive: true,
maxSockets: 100,
maxFreeSockets: 10,
timeout: 60000
})
});
Compression de Réponse
Compression Gzip :
gzip on;
gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;
gzip_min_length 1024;
gzip_comp_level 6;
Intégration avec GitScrum
Dashboard de Monitoring de Gateway
Suivi de Santé d'API Gateway :
- Monitorer le débit et la latence de demande
- Suivre la disponibilité de service et les taux d'erreur
- Configurer des alertes pour les pannes de gateway
Mappage de Dépendance de Service
Visualisation de Dépendance :
API Gateway ──► User Service ──► Auth Service
│ │ │
└──────────────┼───────────────┘
Service Mesh Integration