Guides

Guide d'architecture logicielle

Conception d'API fiables : délais, idempotence, cache et passage à l'échelle

Les API fiables rendent explicites leur comportement de temps, cohérence, capacité et défaillance afin que les appelants réussissent sans submerger les systèmes qui les soutiennent.

Un contrat d'API ne se limite pas à un chemin, une méthode et un schéma JSON. Il définit aussi combien de temps un appelant doit attendre, si une opération peut être répétée, quelles réponses peuvent être mises en cache, comment la capacité est partagée et ce qu'un client doit faire lorsqu'une dépendance n'est pas disponible. Ces choix déterminent si une API reste utile lorsque le trafic ou les défaillances sont réels plutôt qu'idéaux.

Ce guide se concentre sur des contrôles neutres vis-à-vis des fournisseurs qui fonctionnent avec une passerelle, une périphérie managée, un service mesh ou un déploiement direct de service. Les noms de produits peuvent changer ; les exigences architecturales ne le devraient pas.

Une réponse HTTP réussie n'est pas la seule condition de succès

Une API peut répondre rapidement tout en manquant à son contrat si elle expose les données d'un autre tenant, accepte un travail qu'elle ne peut pas achever, répète un prélèvement ou sert une représentation obsolète là où la fraîcheur est requise. Concevez ensemble le temps de réponse, la correction et la sécurité.

Définissez des contrats exploitables par les clients

Documentez le but de chaque opération, son périmètre d'authentification, le schéma d'entrée, la représentation de réponse, le modèle de pagination, le format d'erreur, la limite de débit, l'attente de délai d'expiration et les recommandations de nouvelle tentative. Versionnez intentionnellement : les champs ajoutés sont généralement plus faciles à faire évoluer que les changements sémantiques d'un champ que les clients interprètent déjà.

Utilisez précisément la sémantique HTTP. GET et HEAD sont des opérations de récupération sûres ; PUT et DELETE sont censées être idempotentes, même si une implémentation doit toujours rendre les effets répétés sûrs. POST crée ou déclenche souvent du travail et nécessite une stratégie d'idempotence lorsque les clients peuvent réessayer. Renvoyez des codes de statut qui permettent aux appelants de distinguer une entrée invalide, une autorisation manquante, un conflit, une limitation de débit et une indisponibilité temporaire.

Pour les opérations longues, évitez de maintenir une connexion client ouverte sans résultat borné. Validez et acceptez durablement la requête, renvoyez un identifiant d'opération et un emplacement de statut clair, puis achevez le travail de manière asynchrone. Indiquez l'objectif de réalisation, la période de rétention, la sémantique d'annulation et si une requête dupliquée renvoie l'opération existante.

Budgétez le temps de l'appelant à la dépendance

Choisissez d'abord un objectif de latence côté utilisateur, puis remontez le chemin critique. Une cible API de 500 ms ne peut pas inclure de façon sûre 400 ms de travail de passerelle, une requête de base de données non bornée et deux tiers qui réessaient. Donnez une échéance à chaque requête et propagez le temps restant aux appels en aval.

Définissez des délais d'expiration plus courts plus bas dans la pile afin que l'appelant ait le temps d'utiliser un repli, de renvoyer une erreur ou d'annuler le travail. Une expiration n'est pas la preuve que le travail s'est arrêté : un serveur peut encore écrire après la déconnexion du client. Prenez en charge l'annulation lorsque le runtime et la dépendance le permettent, et rendez les écritures métier idempotentes afin qu'un résultat incertain puisse être rapproché de façon sûre.

Les nouvelles tentatives doivent être exceptionnelles, bornées et guidées par l'opération :

  • Ne réessayez que les défaillances transitoires et seulement tant qu'il reste du temps dans l'échéance de l'appelant.
  • Utilisez un backoff exponentiel avec gigue pour éviter des vagues de nouvelles tentatives synchronisées.
  • Ne réessayez pas les erreurs de validation ou d'autorisation.
  • Exigez une clé d'idempotence ou une identité de requête durable pour les opérations répétées de création, paiement, réservation et mutation.
  • Publiez Retry-After sur les réponses de limitation de débit ou de surcharge lorsqu'une fenêtre de nouvelle tentative pertinente existe.

Les disjoncteurs et bulkheads peuvent contenir une dépendance défaillante, mais leur comportement doit être observable et testé. Un disjoncteur qui s'ouvre trop vite peut créer sa propre panne ; un disjoncteur qui ne s'ouvre jamais ne fait que signaler une défaillance lente. Séparez les pools de concurrence du trafic critique et facultatif afin qu'un rapport coûteux ne consomme pas les ressources nécessaires à la connexion ou au paiement.

Intégrez l'idempotence aux changements d'état

Une clé d'idempotence associe une intention client à un résultat durable. Stockez la clé, le principal authentifié ou le tenant, l'empreinte de la requête, l'état courant et la réponse finale pendant une période de rétention documentée. Si la même clé arrive avec la même intention, renvoyez le résultat d'origine ou courant. Si elle arrive avec une charge utile différente, renvoyez un conflit plutôt que d'appliquer une mutation ambiguë.

N'utilisez pas un cache en mémoire à courte durée de vie comme seul magasin d'idempotence. Les processus redémarrent, les requêtes peuvent arriver sur différentes instances et les nouvelles tentatives différées peuvent survenir après l'expiration d'une entrée de cache. Une contrainte d'unicité de base de données, une transactional outbox ou un magasin durable dédié rend la frontière résiliente.

Pour les événements émis après une écriture, utilisez une outbox ou une remise atomique équivalente afin que le changement d'état et le message ne divergent pas silencieusement. Les consommateurs doivent aussi être idempotents, car une livraison au moins une fois peut produire des doublons. L'ordre doit être une garantie déclarée par clé, et non une hypothèse fondée sur l'heure d'arrivée.

Mettez en cache les lectures en sécurité à la périphérie et au niveau service

Le cache peut supprimer la latence d'origine et protéger la capacité amont, mais il ne doit pas affaiblir l'autorisation ou la fraîcheur. Classez une réponse avant de lui attribuer une politique de cache partagé :

Type de réponseValeur par défaut plus sûreNotes
Ressource publique et versionnéeCache partagé avec fraîcheur longueUtilisez des URL immuables ou un versionnage validé.
Catalogue public ou données de référenceCache partagé avec TTL explicite et validationDéfinissez l'invalidation et le comportement obsolète.
Réponse authentifiée, spécifique à un tenantPrivée ou no-store par défautNe comptez jamais sur un cache partagé pour déduire les frontières des tenants.
Autorisation, solde de compte, état de paiementAucun cache partagé sauf conception formelleLa correction et la confidentialité l'emportent sur un faible gain de latence.

Définissez la clé de cache délibérément. Elle peut inclure le chemin, des paramètres de requête sélectionnés, les en-têtes de représentation, la locale et une partition d'autorisation sûre. N'incluez pas indistinctement des en-têtes ou cookies non bornés, ce qui détruit l'efficacité du cache ; n'omettez pas les dimensions qui peuvent conduire à servir une représentation au mauvais appelant.

Utilisez le regroupement de requêtes pour les clés populaires afin d'empêcher une tempête d'échecs. Placez un origin shield ou une couche d'agrégation contrôlée derrière la périphérie lorsque cela convient à la topologie. Gardez l'origine privée, authentifiez le trafic edge-à-origine, ne validez les en-têtes transmis que depuis des proxys de confiance, et limitez les connexions et la concurrence de l'origine. Un CDN ne protège pas une origine que des attaquants peuvent atteindre directement.

Contrôlez la charge avant qu'elle ne devienne une panne

Les limites de débit allouent une ressource finie. Adaptez-les au risque : un endpoint non authentifié peut nécessiter une limite orientée IP ou réseau, tandis qu'une API authentifiée nécessite normalement des limites par tenant, identifiant, opération et coût. Publiez des en-têtes ou une documentation expliquant la fenêtre de quota et le comportement de réponse, sans révéler de détails de capacité interne facilitant les abus.

La limitation de débit seule n'arrête pas les requêtes valides coûteuses. Combinez-la avec des limites de taille du corps, la validation de schéma, des plafonds de pagination, une complexité maximale de requête lorsque pertinent, des limites de concurrence par route et des files de travail pour les opérations asynchrones. Rejetez tôt le travail à la périphérie ou à la passerelle lorsque la décision ne requiert pas l'origine.

Lorsque la capacité est contrainte, priorisez. Réservez une part pour les contrôles de santé et les transactions critiques, dégradez les enrichissements facultatifs et renvoyez une réponse de surcharge rapide et documentée plutôt que de laisser tous les appelants attendre jusqu'à l'épuisement du pool entier. Testez ce comportement dans des conditions de concurrence réalistes, et pas seulement avec des tests fonctionnels à requête unique.

Observez le comportement de la périphérie au magasin de données

Adoptez un modèle de corrélation et de traçage qui traverse la périphérie, la passerelle, l'application, les files et les dépendances. OpenTelemetry fournit des conventions neutres vis-à-vis des fournisseurs pour la télémétrie HTTP, RPC, de base de données et de messagerie. Enregistrez les modèles de route plutôt que les chemins bruts à forte cardinalité ; incluez l'état du cache, la décision de limitation de débit, la tentative de nouvelle tentative, la source d'expiration, l'attente en file et le résultat de dépendance.

Créez des indicateurs de niveau de service pour la disponibilité, la latence et la correction. Un tableau de bord API utile montre les durées p50, p95 et p99 ; les classes de réponse ; la saturation ; le travail rejeté ; le taux de hit du cache ; l'âge de la file ; et la latence des dépendances. Segmentez par opération, région, version de déploiement et type de client. Masquez les identifiants, en-têtes d'autorisation, données personnelles et corps de requête complets dans la télémétrie normale.

Faites délibérément les choix multi-régions et multi-fournisseurs

Le routage mondial peut rapprocher les appelants d'une périphérie, mais il ne peut pas éliminer le coût de cohérence d'une écriture qui doit atteindre une autorité distante. Gardez une propriété d'écriture claire, placez les réplicas de lecture selon l'obsolescence tolérée et exposez une version de réponse ou un horodatage lorsque les clients doivent raisonner sur la fraîcheur.

L'utilisation de plusieurs passerelles, CDN ou régions cloud peut réduire une dépendance opérationnelle unique. Elle exige également des politiques TLS, API, clés de cache, observabilité, critères de santé de routage et basculement testés cohérents. Construisez sur des interfaces HTTP, DNS, traçage et infrastructure portables, puis documentez les limites spécifiques au fournisseur comme contraintes de déploiement plutôt que de les intégrer dans le contrat API.

Liste de contrôle pour une API fiable

Avant d'exposer une API, vérifiez que les appelants reçoivent des recommandations de délai d'expiration et de nouvelle tentative ; que les mutations ont une idempotence durable ; que les politiques de cache correspondent à la sensibilité des données ; que l'accès à l'origine est restreint ; que les limites de débit, taille, pagination et concurrence sont explicites ; que les réponses de surcharge sont testées ; et que la télémétrie relie une décision edge aux résultats applicatifs et de dépendance.

Références faisant autorité

Concevez des API qui restent fiables sous charge réelle

Échangez avec les experts Optimi sur la diffusion d'API, les contrôles edge, la sécurité de l'origine et l'architecture de performance.

Discuter de l'architecture API