Aller au contenu principal

Patterns de Circuit Breaker pour les Gateways API Expliqués

· 17 minutes de lecture
STOA Team
The STOA Platform Team

Les circuit breakers sont des patterns de résilience critiques qui empêchent les pannes en cascade dans les systèmes distribués en bloquant temporairement les requêtes vers les backends défaillants. Dans les gateways API, ils agissent comme des interrupteurs de sécurité automatiques qui détectent les pannes, arrêtent de transférer le trafic vers les services défaillants, et laissent le temps aux systèmes de récupérer avant de reprendre les opérations normales.

Qu'est-ce qu'un Circuit Breaker ?

Un circuit breaker est un design pattern emprunté à l'électrotechnique qui protège les systèmes distribués contre les pannes en cascade. Tout comme un disjoncteur électrique coupe le courant quand un circuit est surchargé, un circuit breaker de gateway API arrête le trafic vers un service backend défaillant.

Le Problème : les Pannes en Cascade

Sans circuit breakers, un seul backend lent ou défaillant peut déclencher un effet domino :

  1. Le service backend ralentit à cause d'une charge élevée, de problèmes de base de données ou de pannes downstream
  2. Les threads du gateway s'accumulent en attendant des réponses qui n'arrivent jamais ou qui expirent lentement
  3. L'épuisement des ressources survient quand les pools de connexions, les threads et la mémoire se remplissent
  4. Les services sains souffrent car le gateway devient non réactif à tout le trafic
  5. Panne complète du système quand toute l'infrastructure API s'effondre

Prenons l'exemple d'une plateforme e-commerce où le service de paiement tombe en panne. Sans circuit breakers :

Client Request → Gateway (waiting...) → Payment Service (down)
↓ (300 threads blocked)
Client Request → Gateway (waiting...) → Payment Service (down)
↓ (600 threads blocked)
Client Request → Gateway (TIMEOUT) → All Services (unreachable)

Le pool de connexions du gateway est épuisé à attendre un service qui ne répondra pas. Maintenant même les services sains comme le catalogue produits ou les profils utilisateurs deviennent inaccessibles.

La Solution : Échouer Rapidement

Les circuit breakers implémentent une stratégie de "fail fast" (échec rapide). Au lieu d'attendre les timeouts sur chaque requête, le gateway détecte les patterns de panne et rejette immédiatement les requêtes vers les backends défaillants :

Client Request → Gateway (Circuit OPEN) → 503 Service Unavailable (instant)
↓ (0 threads blocked)
Client Request → Gateway (Circuit OPEN) → 503 Service Unavailable (instant)

Cela évite l'épuisement des ressources et permet au système de dégrader gracieusement. Les clients reçoivent des erreurs immédiates qu'ils peuvent gérer, plutôt que de rester suspendus indéfiniment.

Pourquoi les Gateways API ont Besoin de Circuit Breakers

Les gateways API se trouvent au point d'entrée de votre système distribué, ce qui en fait l'endroit idéal pour implémenter les circuit breakers. Voici pourquoi :

1. Détection Centralisée des Pannes

Le gateway voit tous les patterns de trafic sur tous les backends. Il peut détecter :

  • La montée des taux d'erreur (réponses 5xx)
  • L'augmentation de la latence (temps de réponse lents)
  • Les échecs de connexion (connexions refusées, erreurs DNS)
  • Les patterns de timeout (requêtes dépassant les délais)

2. Protection pour Tous les Clients

Un circuit breaker au niveau du gateway protège tous les clients downstream, quelle que soit leur implémentation. Que vous ayez des navigateurs web, des applications mobiles ou des microservices internes effectuant des appels API, ils bénéficient tous de la logique de résilience du gateway.

3. Protection du Service Backend

Quand un backend est sous charge, continuer à envoyer du trafic rend la récupération plus difficile. Les circuit breakers donnent aux services défaillants une marge de récupération en arrêtant temporairement les requêtes entrantes, leur permettant de :

  • Vider les files de travail en attente
  • Restaurer les connexions à la base de données
  • Augmenter les ressources
  • Terminer les transactions en cours

4. Frontières de Panne Observables

Les changements d'état des circuit breakers sont des événements hautement observables. Quand un circuit s'ouvre, cela signale un problème de santé du système que les équipes opérationnelles peuvent investiguer. Cela rend la réponse aux incidents plus rapide et plus ciblée.

Pour une compréhension plus approfondie de comment les circuit breakers s'intègrent dans les architectures modernes de gateways API, consultez notre Guide d'Architecture Gateway API.

Les Trois États : Fermé, Ouvert, Semi-Ouvert

Les circuit breakers fonctionnent comme des machines à états finis avec trois états distincts. Comprendre ces états est crucial pour une configuration efficace.

Diagramme d'État

                    ┌─────────────┐
│ CLOSED │ ◄─── État initial
│ (sain) │
└──────┬──────┘

Seuil de panne
atteint (ex : 5
erreurs consécutives)


┌─────────────┐
┌───────┤ OPEN │
│ │ (panne) │
│ └──────┬──────┘
│ │
Requêtes échouent Fenêtre de
immédiatement sommeil
avec 503 expire (ex :
│ 30 secondes)
│ │
│ ▼
│ ┌─────────────┐
│ │ HALF-OPEN │
│ │ (test) │
│ └──────┬──────┘
│ │
│ ┌─────┴─────┐
│ │ │
│ Seuil de Seuil de
│ succès panne
│ atteint atteint
│ │ │
└────────┴───────────┘

État Fermé (Opération Normale)

Comportement : Toutes les requêtes passent vers le backend. Le circuit breaker surveille les réponses.

Surveillance :

  • Comptage des pannes consécutives
  • Suivi des taux d'erreur sur des fenêtres glissantes
  • Mesure des latences de réponse

Transition vers Ouvert : Quand le seuil de panne est dépassé. Déclencheurs courants :

  • 5+ pannes consécutives
  • Taux d'erreur de 50%+ sur 10 requêtes
  • 3+ timeouts en 30 secondes

Exemple de Configuration (YAML) :

circuitBreaker:
state: closed
failureThreshold: 5
successThreshold: 2
timeout: 30s
monitoringWindow: 60s

État Ouvert (Blocage du Trafic)

Comportement : Toutes les requêtes échouent immédiatement avec 503 Service Unavailable. Aucun appel backend n'est effectué.

Objectif :

  • Prévenir l'épuisement des ressources
  • Donner au backend le temps de récupérer
  • Échouer rapidement pour les clients

Transition vers Semi-Ouvert : Après une fenêtre de sommeil configurable (ex : 30 secondes). Cette fenêtre permet au backend de récupérer avant le début des tests.

Réponse Client :

HTTP/1.1 503 Service Unavailable
Content-Type: application/json
Retry-After: 30

{
"error": "circuit_breaker_open",
"message": "Backend service is temporarily unavailable",
"retry_after_seconds": 30
}

État Semi-Ouvert (Test de Récupération)

Comportement : Un nombre limité de requêtes de test sont autorisées pour vérifier si le backend a récupéré.

Stratégie de Test :

  • Autoriser 1 à 3 requêtes sondes
  • Si toutes réussissent → transition vers Fermé
  • Si l'une échoue → transition vers Ouvert

Exemple de Configuration :

circuitBreaker:
halfOpen:
maxRequests: 3 # Nombre de requêtes de test
successThreshold: 2 # Succès nécessaires pour fermer
timeout: 10s # Timeout par requête de test

Pourquoi c'est Important : Le semi-ouvert évite le problème du "thundering herd". Sans lui, quand la fenêtre de sommeil expire, toutes les requêtes bloquées frapperaient simultanément le backend en cours de récupération, pouvant le faire tomber à nouveau.

Patterns d'Implémentation

Les circuit breakers peuvent être définis à différents niveaux dans votre architecture de gateway. Chacun a ses compromis.

1. Circuit Breakers par Route

Portée : Un circuit breaker par endpoint API.

Cas d'Usage : Contrôle fin quand différentes routes ont des caractéristiques de fiabilité différentes.

Configuration :

routes:
- path: /api/v1/payments
backend: http://payment-service:8080
circuitBreaker:
enabled: true
failureThreshold: 3
timeout: 20s
sleepWindow: 60s

- path: /api/v1/products
backend: http://catalog-service:8080
circuitBreaker:
enabled: true
failureThreshold: 10
timeout: 5s
sleepWindow: 30s

Avantages :

  • Pannes isolées : l'ouverture du circuit /payments n'affecte pas /products
  • Seuils ajustés : les endpoints critiques peuvent avoir des seuils plus stricts
  • Surveillance granulaire : métriques par route pour les équipes opérationnelles

Inconvénients :

  • Complexité de configuration : chaque route a besoin d'un ajustement
  • Surcharge mémoire : un état de circuit breaker par route

2. Circuit Breakers par Backend

Portée : Un circuit breaker par service backend (partagé entre toutes les routes vers ce service).

Cas d'Usage : Quand les routes vers le même backend doivent partager la détection de panne.

Configuration :

backends:
payment-service:
url: http://payment-service:8080
circuitBreaker:
enabled: true
failureThreshold: 5
timeout: 30s

routes:
- path: /api/v1/payments/create
backend: payment-service

- path: /api/v1/payments/status
backend: payment-service

Avantages :

  • Configuration simplifiée : une définition de circuit breaker par service
  • État de panne partagé : si le backend est down, toutes les routes arrêtent de l'appeler
  • Empreinte mémoire réduite

Inconvénients :

  • Granularité faible : une route lente peut déclencher le circuit pour toutes les routes
  • Moins de flexibilité : impossible d'ajuster le comportement par route

3. Circuit Breakers Globaux

Portée : Un circuit breaker protégeant l'intégralité du gateway.

Cas d'Usage : Coupure d'urgence pour protéger le gateway lui-même de la surcharge.

Configuration :

global:
circuitBreaker:
enabled: true
failureThreshold: 100 # Seuil élevé
errorRate: 0.5 # Taux d'erreur de 50%
monitoringWindow: 10s
sleepWindow: 60s

Avantages :

  • Protection ultime : empêche l'effondrement total du gateway
  • Simple à raisonner : un état pour l'ensemble du système

Inconvénients :

  • Trop grossier : les backends sains souffrent quand d'autres tombent
  • Rarement utilisé : typiquement un mécanisme de dernier recours

Approche Hybride Recommandée

Dans les systèmes de production, combinez les circuit breakers par backend et globaux :

# Circuit breakers par backend pour l'isolation au niveau service
backends:
payment-service:
circuitBreaker:
failureThreshold: 5
timeout: 30s
catalog-service:
circuitBreaker:
failureThreshold: 10
timeout: 10s

# Circuit breaker global comme coupure d'urgence
global:
circuitBreaker:
failureThreshold: 1000
errorRate: 0.8
monitoringWindow: 30s

Cela assure l'isolation au niveau service tout en protégeant contre les pannes catastrophiques.

Pour plus de patterns sur les architectures de gateways résilientes, consultez Patterns de Gateway API Natif Kubernetes.

Bonnes Pratiques de Configuration

Une configuration efficace des circuit breakers nécessite de comprendre vos patterns de trafic et vos modes de panne.

1. Définir les Seuils de Panne en Fonction du Volume de Trafic

Routes à faible trafic (< 10 requêtes/minute) :

failureThreshold: 3  # 3 pannes consécutives
timeout: 60s

Routes à fort trafic (> 100 requêtes/minute) :

failureThreshold: 10
slidingWindow: 20 # Surveiller les 20 dernières requêtes
errorRate: 0.5 # Ouvrir à 50% de taux d'erreur
timeout: 30s

Pourquoi : Les routes à faible trafic ont besoin de comptages de pannes consécutives (absolu). Les routes à fort trafic ont besoin de pourcentages de taux d'erreur (relatif) pour éviter les faux positifs dus aux pannes occasionnelles.

2. Ajuster les Fenêtres de Sommeil au Temps de Récupération du Backend

Services à récupération rapide (microservices sans état) :

sleepWindow: 10s  # Réessai rapide

Services à récupération lente (bases de données, systèmes legacy) :

sleepWindow: 120s  # Donner plus de temps de récupération

Pourquoi : Les fenêtres de sommeil devraient correspondre aux patterns de récupération observés de votre backend. Trop courtes et vous frapperez répétitivement un service défaillant. Trop longues et vous prolongerez les pannes.

3. Configurer les Requêtes de Test en Semi-Ouvert

Test conservateur :

halfOpen:
maxRequests: 1
successThreshold: 3 # 3 succès nécessaires pour fermer

Test agressif :

halfOpen:
maxRequests: 5
successThreshold: 3 # 3 succès sur 5 pour fermer

Pourquoi : Le test conservateur (1 requête à la fois) est plus sûr pour les backends critiques. Le test agressif (plusieurs sondes simultanées) accélère la détection de récupération.

4. Aligner les Timeouts avec la Logique du Circuit Breaker

Le timeout du circuit breaker devrait être plus court que le timeout de la requête :

request:
timeout: 10s # Délai global de la requête

circuitBreaker:
timeout: 8s # Timeout du circuit breaker (plus court)
failureThreshold: 3

Pourquoi : Si le timeout du circuit breaker est plus long que le timeout de la requête, la requête échouera à cause du timeout avant que le circuit breaker ne la compte comme une panne.

5. Utiliser des Seuils Différents pour les Différents Types d'Erreur

Certaines erreurs sont répétables, d'autres non :

circuitBreaker:
failureThreshold: 5

# Erreurs qui déclenchent le circuit
triggerOn:
- 500 # Internal Server Error
- 502 # Bad Gateway
- 503 # Service Unavailable
- 504 # Gateway Timeout
- connection_refused
- timeout

# Erreurs qui ne déclenchent pas le circuit
ignoreOn:
- 400 # Bad Request (erreur client)
- 401 # Unauthorized (erreur d'auth)
- 404 # Not Found (erreur de routage)

Pourquoi : Les erreurs client (4xx) indiquent généralement de mauvaises requêtes, pas des pannes backend. Seules les erreurs serveur (5xx) et les pannes réseau devraient compter dans les seuils du circuit breaker.

Exemple : Configuration Prête pour la Production

routes:
- path: /api/v1/payments/charge
backend: payment-service
circuitBreaker:
enabled: true

# Détection de panne
failureThreshold: 5
slidingWindow: 10
errorRate: 0.5

# Timeouts
timeout: 8s
sleepWindow: 30s

# Test semi-ouvert
halfOpen:
maxRequests: 2
successThreshold: 2

# Classification des erreurs
triggerOn:
- 500
- 502
- 503
- 504
- timeout
- connection_error

# Surveillance
metrics:
enabled: true
labels:
service: payment
criticality: high

Pour des options complètes de configuration du gateway, consultez la Référence de Configuration.

Surveillance et Alertes

Les circuit breakers ne sont efficaces que si vous surveillez leurs changements d'état et répondez aux incidents.

Métriques Clés à Suivre

Métriques d'État du Circuit :

circuit_breaker_state{route="/api/v1/payments", backend="payment-service"}
Valeurs : 0 (fermé), 1 (ouvert), 2 (semi-ouvert)

circuit_breaker_state_changes_total{route="/api/v1/payments", backend="payment-service", from="closed", to="open"}
Compteur : s'incrémente quand le circuit s'ouvre

Métriques de Panne :

circuit_breaker_failures_total{route="/api/v1/payments", backend="payment-service"}
Compteur : total des pannes détectées

circuit_breaker_consecutive_failures{route="/api/v1/payments", backend="payment-service"}
Gauge : nombre actuel de pannes consécutives

Métriques de Requête :

circuit_breaker_requests_total{route="/api/v1/payments", backend="payment-service", result="success|failure|rejected"}
Compteur : total des requêtes par résultat

circuit_breaker_request_duration_seconds{route="/api/v1/payments", backend="payment-service"}
Histogramme : distribution de latence des requêtes

Règles d'Alertes

Alerte Critique : Circuit Ouvert

- alert: CircuitBreakerOpen
expr: circuit_breaker_state{criticality="high"} == 1
for: 1m
labels:
severity: critical
annotations:
summary: "Circuit breaker ouvert pour {{ $labels.route }}"
description: "Le backend {{ $labels.backend }} est défaillant. Circuit ouvert pour protéger le système."

Alerte Avertissement : Changements d'État Fréquents

- alert: CircuitBreakerFlapping
expr: rate(circuit_breaker_state_changes_total[5m]) > 0.1
for: 5m
labels:
severity: warning
annotations:
summary: "Circuit breaker instable pour {{ $labels.route }}"
description: "Circuit changeant d'état fréquemment. Le backend peut être instable."

Exemple de Tableau de Bord

Un tableau de bord circuit breaker de production devrait inclure :

  1. Panneau d'État Global : État actuel de tous les circuits (vert = fermé, rouge = ouvert, jaune = semi-ouvert)
  2. Timeline des États : Graphique montrant les changements d'état dans le temps
  3. Panneau de Taux d'Erreur : Taux d'erreur par backend avec ligne de seuil
  4. Panneau de Latence : Percentiles de temps de réponse (p50, p95, p99)
  5. Panneau de Volume de Requêtes : Requêtes acceptées vs rejetées
  6. Panneau de Durée dans l'État : Combien de temps chaque circuit est dans son état actuel

Exemple de Requête Prometheus :

# États du circuit breaker sur tous les backends
sum by (backend, state) (circuit_breaker_state)

# Taux d'erreur sur les 5 dernières minutes
rate(circuit_breaker_failures_total[5m])
/ rate(circuit_breaker_requests_total[5m])

# Taux de rejet de requêtes
rate(circuit_breaker_requests_total{result="rejected"}[1m])

Pour plus de patterns d'observabilité, consultez le Guide de Renforcement de la Sécurité Gateway API qui couvre la surveillance des événements de sécurité.

Exemple Concret : Plateforme E-Commerce

Parcourons un scénario pratique de circuit breakers en action.

Architecture du Système

Client → API Gateway → [ Payment Service ]
→ [ Inventory Service ]
→ [ Notification Service ]

Scénario : Panne de Base de Données du Service de Paiement

Chronologie :

T+0s : La base de données du service de paiement manque de connexions. Les requêtes commencent à expirer.

T+5s : Le gateway détecte 5 erreurs de timeout consécutives sur /api/v1/payments/charge. Le circuit breaker s'ouvre.

# Logs du gateway
[INFO] Circuit breaker OPENED for route=/api/v1/payments/charge backend=payment-service
[INFO] Reason: 5 consecutive timeouts exceeded threshold

T+6s : Les nouvelles requêtes de paiement reçoivent des réponses 503 immédiates :

curl -X POST https://api.example.com/api/v1/payments/charge \
-H "Content-Type: application/json" \
-d '{"amount": 100, "currency": "USD"}'

# Réponse (instantanée, sans appel backend)
HTTP/1.1 503 Service Unavailable
Retry-After: 30

{
"error": "circuit_breaker_open",
"message": "Payment service is temporarily unavailable",
"retry_after_seconds": 30
}

T+35s : La fenêtre de sommeil expire. Le circuit passe en semi-ouvert.

T+36s : Le gateway envoie une requête de test au service de paiement. Le backend a récupéré. La requête réussit.

T+37s : La deuxième requête de test réussit.

T+38s : Seuil de succès atteint (2/2 requêtes de test réussies). Le circuit se ferme.

# Logs du gateway
[INFO] Circuit breaker CLOSED for route=/api/v1/payments/charge backend=payment-service
[INFO] Reason: 2 consecutive successes in half-open state

T+39s : Les opérations normales reprennent. Toutes les requêtes de paiement passent.

Ce qui s'est Passé en Coulisses

  1. Protection des Ressources : Pendant les 30 secondes d'état ouvert, le gateway n'a pas gaspillé de threads ou de connexions en essayant d'atteindre le service de paiement défaillant. Les services d'inventaire et de notification ont continué à fonctionner normalement.

  2. Échec Rapide : Les clients ont reçu des erreurs immédiates qu'ils pouvaient gérer (ex : afficher un message "paiement temporairement indisponible") au lieu de rester suspendus 30+ secondes à attendre des timeouts.

  3. Récupération Automatique : Le mécanisme de test semi-ouvert a détecté la récupération du service de paiement et a automatiquement rétabli les opérations normales sans intervention humaine.

  4. Observabilité : L'équipe opérationnelle a reçu une alerte "Circuit Ouvert" à T+5s, a investigué la base de données du service de paiement et a résolu le problème de pool de connexions.

Pour des benchmarks de performance de la surcharge des circuit breakers, consultez STOA Gateway Performance Benchmarks.

Circuit Breakers dans les Écosystèmes Modernes de Gateways API

Les circuit breakers ne sont qu'un composant d'une architecture de gateway API résiliente. Ils fonctionnent mieux combinés avec :

  • Timeouts : Empêchent les requêtes de rester suspendues indéfiniment
  • Tentatives avec backoff exponentiel : Gèrent les pannes transitoires
  • Rate limiting : Empêche la surcharge avant qu'elle ne cause des pannes
  • Health checks : Détection proactive des problèmes de backend
  • Request hedging : Envoi de requêtes dupliquées pour réduire la latence de queue
  • Isolation bulkhead : Pools de threads séparés par backend

Pour une vue complète de ces patterns, consultez notre Glossaire Gateway API et Guide Gateway API Open Source.

FAQ

Comment les circuit breakers diffèrent-ils des tentatives de répétition ?

Les circuit breakers empêchent les requêtes d'atteindre les backends défaillants, tandis que les tentatives de répétition essayent les requêtes plusieurs fois. Ils servent des objectifs complémentaires :

  • Les tentatives de répétition gèrent les pannes transitoires (coupures réseau temporaires, requête unique échouée). Elles sont pertinentes quand la panne est aléatoire et que la prochaine tentative peut réussir.
  • Les circuit breakers gèrent les pannes persistantes (crash de service, base de données down). Quand un backend est vraiment cassé, réessayer gaspille des ressources. Les circuit breakers détectent le pattern et arrêtent d'essayer les requêtes.

En pratique, utilisez les deux : configurez des tentatives de répétition (2-3 essais avec backoff exponentiel) pour les pannes transitoires, et des circuit breakers pour détecter quand les tentatives ne sont plus utiles.

Que se passe-t-il pour les requêtes en cours quand un circuit s'ouvre ?

Les requêtes en cours (celles déjà envoyées au backend avant l'ouverture du circuit) sont autorisées à se terminer. Le circuit breaker n'affecte que les nouvelles requêtes entrantes. Cela évite d'annuler brusquement des requêtes qui pourraient réussir.

Cependant, configurez votre gateway pour respecter les timeouts de requêtes même pour les requêtes en cours. Si une requête dépasse son timeout, le gateway devrait l'annuler et retourner une erreur au client, quel que soit l'état du circuit.

Comment tester les circuit breakers dans des environnements de staging ?

Tester efficacement les circuit breakers nécessite de simuler des scénarios de panne :

  1. Chaos engineering : Utilisez des outils comme Chaos Monkey pour tuer aléatoirement des pods backend ou injecter de la latence. Vérifiez que les circuits s'ouvrent comme prévu.

  2. Pannes synthétiques : Créez des endpoints de test sur vos backends qui retournent des erreurs à la demande. Exemple : GET /test/circuit-breaker?error_rate=0.5 retourne 50% d'erreurs.

  3. Tests de charge : Utilisez des outils comme k6 ou Gatling pour envoyer des volumes élevés de requêtes en introduisant des pannes backend. Surveillez les transitions d'état du circuit.

  4. Déclenchement manuel : Certains gateways supportent des APIs d'administration pour ouvrir/fermer manuellement les circuits à des fins de test : POST /admin/circuit-breaker/payments/open.

Testez à la fois le chemin nominal (le circuit reste fermé pendant les opérations normales) et les scénarios de panne (le circuit s'ouvre, passe en semi-ouvert, puis se referme).

Prochaines Étapes

Les circuit breakers sont un pattern de résilience fondamental pour les gateways API de production. Pour continuer à apprendre :

Les circuit breakers n'empêcheront pas toutes les pannes, mais ils garantiront que votre système échoue gracieusement, récupère automatiquement, et offre une meilleure expérience à vos utilisateurs pendant les incidents.