Software Architecture

Passerelles API résilientes : disjoncteurs et limites

Dans les architectures microservices modernes, la passerelle API sert de point d'entrée unique pour tout le trafic client. Cependant, ce rôle centralisé en fait un point de défaillance potentiel unique. Sans mécanismes de protection robustes, une augmentation du trafic ou une panne d'un service en aval peut se propager, entraînant la chute de toute la plateforme. Cet article explore les stratégies de mise en œuvre de disjoncteurs et de limitation de débit pour créer une couche d'entrée multi-protocoles résiliente.

L'importance de la résilience de l'entrée

La résilience ne consiste pas seulement à prévenir les plantages ; il s'agit de maintenir un comportement prévisible sous contrainte. Lorsqu'un service en aval devient lent ou ne répond plus, l'attente d'une expiration de délai pour chaque requête consomme des ressources et bloque les connexions. Une passerelle résiliente détecte ces défaillances et élimine rapidement la charge ou échoue rapidement, protégeant ainsi à la fois l'expérience utilisateur et l'infrastructure interne.

L'entrée multi-protocoles ajoute de la complexité car différents clients peuvent utiliser les protocoles HTTP, gRPC ou WebSocket. Chaque protocole possède des caractéristiques distinctes pour la gestion des connexions et la propagation des erreurs. La passerelle doit normaliser ces différences pour appliquer des politiques de protection cohérentes.

Mise en œuvre des disjoncteurs

Un disjoncteur est un motif de conception qui empêche un système de tenter d'exécuter une opération vouée à l'échec. Il fonctionne généralement dans trois états : Fermé, Ouvert et Demi-ouvert.

  • Fermé : L'état par défaut. Les requêtes passent. Les échecs sont suivis.
  • Ouvert : Déclenché lorsque le seuil d'échec est atteint. Les requêtes sont rejetées immédiatement.
  • Demi-ouvert : Après une période de refroidissement, un nombre limité de requêtes est autorisé pour tester si le service s'est rétabli.

Voici une implémentation conceptuelle d'un disjoncteur en Go, un langage courant pour la construction de passerelles haute performance :

type CircuitBreaker struct {
    mu          sync.RWMutex
    failures    int
    threshold   int
    timeout     time.Duration
    isOpen      bool
    openedAt    time.Time
}

func (cb *CircuitBreaker) AllowRequest() bool {
    cb.mu.RLock()
    defer cb.mu.RUnlock()

    if !cb.isOpen {
        return true
    }

    if time.Since(cb.openedAt) > cb.timeout {
        // Transition to Half-Open
        cb.mu.Lock()
        defer cb.mu.Unlock()
        cb.isOpen = false
        cb.failures = 0
        return true
    }

    return false
}

func (cb *CircuitBreaker) RecordFailure() {
    cb.mu.Lock()
    defer cb.mu.Unlock()

    cb.failures++
    if cb.failures >= cb.threshold && !cb.isOpen {
        cb.isOpen = true
        cb.openedAt = time.Now()
    }
}

Pour la prise en charge multi-protocoles, assurez-vous que la définition de l'échec est cohérente. Pour gRPC, un code de statut « Unavailable » peut déclencher un échec, tandis que pour HTTP, un statut 503 ou une expiration de délai serait le déclencheur.

Stratégies de limitation de débit

La limitation de débit contrôle le nombre de requêtes qu'un client peut effectuer vers une API dans un intervalle de temps spécifié. Cela empêche tout client unique de monopoliser les ressources. Les algorithmes courants incluent le seau à jetons (Token Bucket) et le seau fuyard (Leaky Bucket).

L'algorithme du seau à jetons est souvent préféré pour les passerelles API car il permet des rafales de trafic. Chaque client se voit allouer un seau avec un nombre fixe de jetons. Des jetons sont ajoutés au seau à un taux constant. Chaque requête consomme un jeton. Si le seau est vide, la requête est rejetée.

Lors de la mise en œuvre de la limitation de débit pour une entrée multi-protocoles, tenez compte des éléments suivants :

  1. Limites par client : Utilisez des clés API ou des adresses IP pour identifier les clients. Les connexions WebSocket, qui sont de longue durée, doivent avoir des limites différentes de celles des requêtes HTTP de courte durée.
  2. Limites globales : Mettez en œuvre une limite globale pour protéger toute l'infrastructure contre les pics de trafic inattendus.
  3. Fenêtre glissante : Utilisez une fenêtre glissante au lieu d'une fenêtre fixe pour éviter les cas limites où un client pourrait doubler le taux de requêtes en les effectuant juste avant et juste après le réinitialisation d'une fenêtre.

Les systèmes distribués nécessitent un magasin d'état centralisé, tel que Redis, pour suivre les comptes de jetons entre plusieurs instances de passerelle. Cela garantit que les limites de débit sont appliquées de manière cohérente, quelle que soit l'instance qui traite la requête.

Combinaison des stratégies pour une résilience maximale

Les disjoncteurs et la limitation de débit fonctionnent le mieux lorsqu'ils sont utilisés ensemble. La limitation de débit prévient l'abus et la surcharge, tandis que les disjoncteurs protègent contre les défaillances en aval. Lorsqu'un disjoncteur s'ouvre, la passerelle peut renvoyer un « 503 Service Unavailable » ou un code d'erreur personnalisé pour informer le client que le service est temporairement hors ligne. Cela permet au client de mettre en œuvre sa propre logique de recul et de nouvelle tentative.

De plus, envisagez de mettre en œuvre des cloisons (bulkheads) pour isoler différents services ou locataires. Si un service connaît un pic de trafic, il ne devrait pas consommer tous les pools de connexions, privant ainsi les autres services.

Conclusion

La conception d'une passerelle API résiliente est un aspect critique de la construction d'architectures microservices fiables. En mettant en œuvre des disjoncteurs pour gérer les défaillances en aval et une limitation de débit pour contrôler le trafic, vous pouvez vous assurer que votre système reste disponible et performant dans des conditions de charge variables. Testez toujours vos stratégies de résilience sous charge pour vérifier qu'elles se comportent comme prévu. N'oubliez pas, la résilience n'est pas une configuration unique ; c'est un processus continu de surveillance, d'ajustement et d'amélioration.

Share: