Microservices: comment respecter le contrat

La transition vers une architecture de microservices nécessite une révision de l'approche du développement, des tests, de la maintenance, de la conception - en d'autres termes, de tous les aspects du cycle de vie des composants logiciels. Dans cet article, nous parlerons des pratiques que l'équipe d'architectes Acronis a adoptées pour évoluer vers les meilleures API de composants. L'histoire comprendra à la fois l'énoncé du problème et l'analyse de ses solutions. Peut-être, ce post semblera «capitaine» pour certains, il ne sera pas clair pour quelqu'un pourquoi la super-solution X a été manquée, mais nous espérons qu'elle vous sera intéressante et utile. Nous invitons les constructeurs de microservices à lire et à laisser vos commentaires sous cat.

image

Si vous êtes abonné à notre blog, vous avez déjà pris connaissance des contrats de microservices. Nous en avons parlé dans des articles sur le choix de Swagger ou RAML , ainsi que des vérifications statiques qui peuvent être effectuées sur la base d'annotations créées précédemment. La raison de la publication d'aujourd'hui était un rapport de la conférence HighLoad . Nous avions besoin de parler de manière générale du chemin que nous avons suivi pour formaliser la relation entre les microservices. Et aujourd'hui, je veux partager nos conclusions avec Habr, et aussi vérifier si d'autres architectes sont d'accord avec nous.

Les microservices sont les «briques» à partir desquelles les développeurs créent des applications modernes. Chacun de ces services interagit avec le monde extérieur via une API. En règle générale, les microservices sont développés par des équipes distinctes, parfois dispersées géographiquement, donc pour un travail efficace, il est nécessaire de maintenir la cohérence et l'intégrité de leurs interfaces publiques. Dans les grandes entreprises avec des centaines de services, il est nécessaire d'avoir une annotation de chaque composant: formaliser les données d'entrée et décrire en détail les résultats de son travail. Si vous travaillez avec HTTP REST, il existe deux formats d'annotation courants: RAML et Open API Specification (aka Swagger). Mais les questions sur lesquelles nous nous concentrons aujourd'hui ne sont liées à aucun protocole particulier. Par conséquent, ce qui suit sera pertinent même pour gRPC.

Contexte


Acronis existe depuis plus de 15 ans. Pendant ce temps, les produits et la base de code ont considérablement évolué. D'une application de bureau complexe, nous sommes arrivés à un modèle d'entreprise avec des consoles de gestion centralisées, une délimitation des droits et des journaux d'audit. L'étape suivante a été la transformation de l'application d'entreprise en une plate-forme ouverte, où l'expérience accumulée a été utilisée pour s'intégrer aux services externes.

Si auparavant l'API était importante, elle est désormais devenue un composant essentiel du produit. Et les processus fournis par cette API ont mûri.

Problèmes majeurs


Les problèmes entourant la construction d'API semblent familiers à tout le monde. Nous les décrirons sous une forme hypertrophiée: comme des douleurs qui tourmentent un hypothétique programmeur Vasya et son non moins hypothétique manager Kolya. Tous les noms sont fictifs et les correspondances ne sont pas accidentelles :)

1. La description est obsolète

Laissez le programmeur Vasya développer le composant A, qui utilise l'API du composant B. Ce dernier a une annotation, mais elle n'est pas valide. Vasya doit ramper dans le code de quelqu'un d'autre, chercher des gens, poser des questions. Les délais se rapprochent, et son manager Kolya doit faire face aux transferts de délais.

2. L'API n'est pas cohérente

Le programmeur Vasya a terminé la tâche et est passé à la suivante liée au fonctionnement du composant B.Mais les développeurs B et les développeurs C ont un sentiment de beauté différent, donc les mêmes choses se font différemment dans l'API. Vasya est de nouveau allé traiter le code, et Kolya souffre à nouveau d'un non-respect des délais.

3. API non documentée

Le manager de Kolya décide de publier l'API du composant A afin que les intégrateurs puissent faire de merveilleuses intégrations. Les intégrateurs sont confrontés à des problèmes, le service d'assistance est surchargé, tout est en feu avec le manager Kolya, et Vasya sent que son tour viendra bientôt.

4. L'API n'est pas compatible avec l'ancienne version.

Les intégrations sont mises en œuvre, tous les incendies sont éteints. Mais Vasya décide soudain que l'API de son composant est loin d'être parfaite et est plongée dans la révision. Bien sûr, dans ce cas, la compatibilité descendante est violée et toutes les intégrations s'effondrent. Cette situation entraîne des coûts pour les intégrateurs et une perte d'argent pour la société de développement.

Méthodes de traitement


Tous ces problèmes surviennent lorsque les programmeurs n'ont aucune idée d'une bonne API REST ou que cette vue est fragmentée. En réalité, tous les développeurs n'ont pas d'expérience avec REST. Et donc, les principales méthodes de «traitement» visent l'éducation. Lorsque la vision de la bonne API, coordonnée avec la vision d'autres développeurs, architectes et documentaires, commence à se faufiler dans la tête de chaque développeur, l'API devient idéale. Le processus de formation de cette vision nécessite des efforts et des outils spécialisés, dont nous allons maintenant parler.

Douleur 1. L'annotation ne correspond pas à l'implémentation


L'annotation peut différer de l'état actuel du service non seulement parce qu'il s'agit d'une API du «passé sombre», qui ne peut pas être atteinte. Il peut également s'agir d'une future API brillante qui n'est pas encore arrivée.

La raison de ces conditions est un manque de compréhension des raisons pour lesquelles les annotations sont nécessaires. Sans terreur de la part des architectes, les développeurs ont tendance à considérer l'annotation comme un outil auxiliaire interne et n'impliquent pas que quelqu'un d'extérieur l'utilisera.

Vous pouvez guérir cette douleur en:

  • Revue architecturale. Une chose très utile pour les entreprises de toutes tailles, dans lesquelles il y a au moins un programmeur qui «sait comment bien faire». Lors du changement de service, l'architecte ou le responsable doit surveiller l'état des annotations et rappeler aux programmeurs qu'il est nécessaire de mettre à jour non seulement le service, mais aussi sa description. Effets secondaires - un goulot d'étranglement face à l'architecte
  • Génération de code à partir d'annotations. Il s'agit de la soi-disant approche API-first. Cela implique que vous fassiez d'abord l'annotation, puis générez le code principal (il y a suffisamment d'outils pour cela, par exemple [go-swagger] (https://github.com/go-swagger/go-swagger)), puis remplissez le service-entreprise logique. Cette disposition évite les incohérences. Cela fonctionne bien lorsque le domaine des tâches résolues par le service est clairement délimité.
  • Test des annotations par rapport à l'implémentation . Pour ce faire, nous générons à partir de l'annotation (RAML / swagger) le client qui bombarde le service de requêtes. Si les réponses correspondent à l'annotation et que le service lui-même ne tombe pas, alors tout va bien.

Test d'annotation vs d'implémentation
Arrêtons-nous sur les tests. Cette génération de requêtes entièrement automatique est une tâche complexe. Ayant des données d'annotations API, vous pouvez créer des demandes distinctes. Cependant, toute API implique des dépendances, par exemple, avant d'appeler GET / clients / {cliend_id}, vous devez d'abord créer cet objet, puis obtenir son identifiant. Parfois, les dépendances sont moins explicites - la création d'un objet X nécessite de passer l'identifiant de l'objet associé Y, et ce n'est pas une sous-collection. Ni RAML ni Swagger ne permettent de décrire des dépendances explicites. Par conséquent, plusieurs approches sont possibles ici:

  1. Attendez-vous à des commentaires formels de la part des développeurs dans des annotations indiquant des dépendances.
  2. Demandez une description de la séquence attendue aux développeurs (il existe plusieurs façons de décrire les demandes en utilisant YAML , DSL spécialisé ou via une belle interface graphique, comme l'a fait l'apigée maintenant abandonnée.
  3. Prenez des données réelles (par exemple, en utilisant OpenResty pour enregistrer toutes les demandes et réponses du serveur)
  4. Extraire les dépendances de l'annotation en utilisant (presque) l'intelligence artificielle (par exemple RESTler )

Dans tous les cas, la tâche de test prend beaucoup de temps.

image

Personnellement, nous sommes arrivés au point de préparer manuellement des séquences de test. Dans tous les cas, les développeurs doivent écrire des tests, afin que nous puissions leur fournir un outil pratique qui peut trouver quelques bogues supplémentaires.

Notre utilitaire utilise le yaml suivant pour décrire la séquence de demandes:

image

Les accolades indiquent les variables qui sont substituées pendant les tests. La variable d'adresse est transmise en tant que paramètre CLI et aléatoire génère une chaîne arbitraire. Le champ response-to-var est le plus intéressant: il contient une variable dans laquelle json sera écrit avec la réponse du serveur. Ainsi, dans la dernière ligne, vous pouvez obtenir l'id de l'objet créé en utilisant task.id.

Douleur 2. L'API n'est pas cohérente


Qu'est-ce que la cohérence? Nous n'introduirons aucune définition formelle, mais, pour simplifier, il s'agit de cohérence interne. Par exemple, dans le projet initial, Vasya avait besoin d'agréger les données des rapports sur HighLoad, et l'API fournit un filtrage des données par année. Une fois le projet presque terminé, le responsable Kolya est venu à Vasya avec une demande pour ajouter des statistiques sur les locuteurs à l'analyse et pour créer une nouvelle méthode «GET speakers» également avec filtrage par année. En conséquence, Vasya complète le code en quelques heures, mais au cours du processus de test, il s'avère que la méthode ne fonctionne pas. La raison en est que dans un cas, «année» est un nombre, dans un autre, une chaîne. Mais cela, bien sûr, n'est pas évident à première vue et nécessite un soin constant lorsque vous travaillez avec l'API. La persistance de l'API est lorsqu'un tel soin excessif n'est pas requis.

Il existe de nombreux exemples d'incohérence:

  1. utilisation de différents formats des mêmes données. Par exemple, format d'heure, type d'identifiant (numéro ou chaîne UUID),
  2. appliquer une syntaxe différente pour le filtrage ou la pagination,
  3. différents régimes d'autorisation pour les services. Non seulement les cerveaux des programmeurs poudrent les différences, mais ils réfléchissent également aux tests qui devront prendre en charge différents schémas.


Traitement :

  • Revue architecturale. S'il y a un architecte tyran, il (en l'absence de schizophrénie) assurera la cohérence. Effets secondaires: facteur bus et tyrannie :)
  • Création de l'API Guideline. Il s'agit d'un standard unique qui doit être développé (ou préparé), mais le plus important est de le mettre en œuvre. Cela nécessite de la propagande, un bâton et une carotte.
  • Implémentation de contrôles statiques pour la conformité avec la directive API d'annotation (lire à ce sujet ici ).

image
Exemple - Éléments de test statiques

Chaque entreprise fait son propre choix de directives à utiliser. Et, probablement, il n'y a pas d'approche universelle, ce qui devrait être et ce qui ne devrait pas. Après tout, plus il y a de dispositions dans la norme, plus vous contrôlez et plus vous restreignez la liberté de créativité. Et surtout, peu de gens ont lu jusqu'au bout un document de «seulement 100 pages».

Dans notre entreprise, nous avons inclus les points suivants dans la directive:

image
D'autres bons exemples de directives peuvent être trouvés sur Microsoft , PayPal , Google .

Douleur 3. API non documentée


L'existence d'annotations est une condition nécessaire mais non suffisante pour la simplicité de travail avec l'API. Vous pouvez écrire une annotation afin qu'elle ne réalise pas son plein potentiel. Cela se produit lorsque:

  1. pas assez de descriptions (pour les paramètres, les en-têtes, les erreurs, etc.);
  2. il n'y a pas assez d'exemples d'utilisation, car l'exemple peut être utilisé non seulement pour améliorer la documentation (plus de contexte pour le développeur et la possibilité de jouer directement avec l'API directement depuis le portail), mais aussi pour les tests (comme point de départ pour le fuzzing));
  3. il existe des fonctionnalités non documentées.

Habituellement, cela se produit lorsque les développeurs ne comprennent pas clairement pourquoi les annotations sont nécessaires, lorsqu'il n'y a pas de communication entre les rédacteurs techniques et les programmeurs, et aussi si personne n'a compris combien coûte l'entreprise avec une mauvaise documentation. Et s'ils venaient au programmeur et tiraient après chaque demande d'assistance, toutes les annotations seraient remplies très rapidement.

Traitement:

  • Disponibilité d'outils de génération de références API pour le programmeur. Si le développeur voit à quoi ressemble la description de son API pour ses collègues et les utilisateurs, il essaiera d'améliorer l'annotation. Effets secondaires: la configuration de ces outils nécessitera un travail manuel supplémentaire.
  • Mise en place d'une interaction entre tous les acteurs impliqués : programmeurs, évangélistes, personnel de soutien. Effets secondaires: rencontrer tout le monde avec tout le monde, compliquer les processus.
  • Utilisation de tests basés sur l'annotation d'API . Implémentation des vérifications statiques ci-dessus dans les référentiels CI avec annotations.

Dans Acronis, une API d'annotation est générée sur la base d'annotations avec les clients SDK et les sections Try-It. Avec des exemples de code et des descriptions de cas d'utilisation, ils forment une gamme complète de modules complémentaires nécessaires et pratiques pour les programmeurs. Voir notre portail à developer.acronis.com

image

Je dois dire qu'il existe toute une classe d'outils pour générer la référence API. Certaines entreprises développent elles-mêmes ces outils pour leurs propres besoins. D'autres utilisent des outils assez simples et gratuits comme l' éditeur Swagger . Après une longue (très longue) recherche, nous, chez Acronis, nous sommes installés sur Apimatic.io, le préférant à REST United, Mulesoft AnyPoint et autres.

Pain 4. Problèmes de compatibilité descendante


La rétrocompatibilité peut être altérée en raison de toute bagatelle. Par exemple, le programmeur Vasya écrit à chaque fois le mot compatibilité avec une faute de frappe: compatibilité. Cette faute de frappe se trouve dans le code, dans les commentaires et dans un paramètre de requête. Ayant remarqué une erreur, Vasya fait un remplacement de ce mot tout au long du projet et sans regarder envoie les modifications à la production. Bien sûr, la compatibilité descendante sera compromise et le service baissera pendant plusieurs heures.

Pourquoi de tels événements peuvent-ils même se produire? La raison principale est une mauvaise compréhension du cycle de vie de l'API, qui peut se manifester par la rupture des intégrations, des politiques EOL (End Of Life) imprévisibles et des versions d'API obscures.

Traitement:

  • Revue architecturale. Comme toujours, la main ferme d'un architecte peut empêcher la rétrocompatibilité. Cependant, sa tâche principale est d'expliquer le coût de la prise en charge de plusieurs versions et de rechercher des options pour apporter des modifications sans casser l'API existante.
  • Vérification de la compatibilité descendante. Si l'annotation de l'API contient une description à jour, les violations de compatibilité descendante peuvent être vérifiées à l'étape CI;
  • Mise à jour en temps opportun de la documentation. La référence et la description de l'API doivent être mises à jour en même temps que le code de service change. Pour ce faire, vous pouvez au moins démarrer des listes de contrôle standardisées, au moins configurer des notifications de changements, au moins former des super-capacités pour tout générer à partir de tout ... Important! Le service de documentation doit être au courant de toutes les modifications prévues afin de pouvoir planifier des ressources pour la mise à jour de la documentation et la rédaction de guides de mise à niveau. Le guide de mise à niveau, testé et vérifié, est un triste attribut de tout changement de nom que vous avez commencé dans l'API.

Gestion du changement


Les règles qui décrivent les activités associées au cycle de vie de l'API sont appelées politiques de gestion des changements.

image

Si vous avez deux versions des annotations «actuelle» et «nouvelle», la vérification de la compatibilité descendante est techniquement simple: il suffit d'analyser les deux annotations et de vérifier si les champs nécessaires existent

image

Nous avons écrit un outil spécial qui vous permet de comparer tous les paramètres critiques pour la compatibilité descendante dans CI. Par exemple, lorsque vous modifiez le corps de la réponse dans la demande GET / healthcheck, un message similaire au suivant s'affiche:

image

Conclusion


Chaque architecte rêve de se débarrasser des problèmes d'API. Chaque manager rêve de ne pas connaître les problèmes d'API. :). Il existe de nombreux médicaments, mais chacun a son propre prix et ses effets secondaires. Nous avons partagé nos options de traitement pour les maladies infantiles les plus simples avec l'API, puis des problèmes plus graves se posent. Conclusions de notre article «capitaines»: les problèmes d'API commencent par la tête et enseigner aux gens les bonnes pratiques est la principale garantie de succès. Tout le reste n'est qu'une question de technologie. Et quels problèmes avez-vous rencontrés et quels moyens de solution avez-vous choisis dans votre organisation?

image
Médicaments pour la mauvaise API.

Nous serons heureux de toutes vos pensées, notes, commentaires, opinions et questions!

Source: https://habr.com/ru/post/fr483380/


All Articles