Workflows transactionnels dans une architecture de microservices sur Google Cloud

Last reviewed 2022-04-04 UTC

Dans une application d'entreprise traditionnelle, les requêtes des clients sont exécutées dans une transaction de base de données. En règle générale, toutes les données dont vous avez besoin pour effectuer une requête sont stockées dans une base de données unique dotée de propriétés ACID (le terme ACID signifie "atomicité, cohérence, isolation, durabilité"). Par conséquent, vous pouvez garantir la cohérence en utilisant les fonctionnalités transactionnelles du système de base de données relationnelle. En cas de problème lors de la transaction, le système de base de données peut effectuer un rollback et réessayer automatiquement. Ce document explique comment concevoir des workflows transactionnels à l'aide de Cloud Run, Pub/Sub, Workflows et Firestore en mode Datastore (Datastore). Il est destiné aux développeurs d'applications qui souhaitent concevoir des workflows transactionnels dans une application basée sur des microservices.

Ce document fait partie d'une série composée des parties suivantes :

Transactions de bout en bout dans des microservices

Dans les architectures de microservices, une transaction de bout en bout peut couvrir plusieurs services. Chaque service peut offrir une fonctionnalité spécifique et sa propre base de données indépendante, comme illustré dans le schéma suivant.

Les services A, B et C avec leurs bases de données respectives.

Comme le montre l'image précédente, un client accède à plusieurs microservices via une passerelle. En raison de ce type d'accès client, vous ne pouvez pas compter sur les fonctionnalités transactionnelles d'une seule base de données pour aider votre système de base de données à récupérer des pannes et à garantir la cohérence. À la place, nous vous recommandons de mettre en œuvre un workflow transactionnel dans votre architecture de microservices.

Ce document décrit deux modèles que vous pouvez utiliser pour mettre en œuvre un workflow transactionnel dans une architecture de microservices. Les modèles sont les suivants :

  • Modèle Saga basé sur la chorégraphie
  • Modèle d'orchestration synchrone

Exemple d'application

Pour illustrer le workflow, ce document utilise un exemple simple d'application qui peut gérer les transactions de commande d'un site Web d'achat. L'application gère les clients et les commandes. Les clients ont une limite de crédit, et l'application doit confirmer qu'une nouvelle commande ne dépassera pas la limite de crédit du client. Comme le montre le schéma suivant, le workflow transactionnel s'exécute sur les microservices suivants : le service Order et le service Customer.

Diagramme de l'architecture de l'exemple d'application.

Le workflow se déroule comme suit :

  • Un client envoie une requête de commande qui spécifie un ID client et un certain nombre d'articles.
  • Le service Order attribue un ID de commande et stocke les informations de la commande dans la base de données. L'état de la commande est marqué comme pending.
  • Le service Customer augmente l'utilisation du crédit du client qui est stockée dans la base de données en fonction du nombre d'articles commandés (par exemple, une augmentation de 100 crédits pour un seul article).
  • Si l'utilisation totale du crédit est inférieure ou égale à la limite prédéfinie, la commande est acceptée et le service Order fait passer à accepted l'état de la commande dans la base de données.
  • Si l'utilisation totale du crédit est supérieure à la limite prédéfinie, le service Order remplace l'état de la commande par rejected. Dans ce cas, l'utilisation du crédit n'est pas augmentée.

Description du service "Order"

Le service Order gère l'état d'une commande. Il génère et stocke un enregistrement de commande dans la base de données Order pour chaque requête client. L'enregistrement comprend les colonnes suivantes :

  • Order_id : ID de la commande. Cet ID est généré par le service Order.
  • Customer_id : ID client.
  • Number : quantité d'articles dans la commande.
  • Status : état d'une commande.

Description du service "Customer"

Le service Customer gère les crédits client. Il génère et stocke un enregistrement client dans la base de données Customer pour chaque requête client. L'enregistrement comprend les colonnes suivantes :

  • Customer_id : ID du client, qui est généré par le service Customer.
  • Credit : nombre de crédits consommés par le client. Il augmente lorsque le client commande des articles.
  • Limit : limite de crédit individuelle du client. Une commande est refusée lorsque le crédit du client dépasse la limite définie.

Modèle Saga basé sur la chorégraphie

Cette section explique comment mettre en œuvre un modèle de microservices Saga basé sur la chorégraphie dans un workflow transactionnel.

Présentation de l'architecture

Dans un modèle de microservices Saga basé sur la chorégraphie, les microservices fonctionnent comme un système autonome distribué. Lorsqu'un service modifie l'état de sa propre entité, il publie un événement pour en informer les autres services des mises à jour. L'événement de notification déclenche l'action d'autres services. De cette manière, plusieurs services fonctionnent conjointement pour mener à bien un processus transactionnel. La communication entre les microservices est asynchrone. Lorsqu'un service publie un événement, aucune information n'est envoyée au service de publication pour confirmer les services qui reçoivent l'événement ni quand ils le reçoivent.

L'image suivante montre un exemple de modèle de microservices Saga basé sur la chorégraphie.

Modèle de microservices Saga basé sur la chorégraphie.

L'exemple d'architecture présenté dans l'image précédente est le suivant :

  • Cloud Run agit comme un environnement d'exécution de microservices.
  • Pub/Sub agit comme un service de messagerie pour diffuser des événements entre les microservices.
  • Datastore fournit une base de données pour chaque service.

Datastore permet de stocker des événements avant de les publier. Comme expliqué dans le processus de publication d'événements, les microservices stockent les événements au lieu de les publier immédiatement.

Les services Order et Customer stockent d'abord les événements dans la base de données d'événements. Les événements stockés sont ensuite publiés régulièrement à l'aide de Cloud Scheduler. Cloud Scheduler appelle le service event-publisher, qui publie les événements. Ce flux d'événements est illustré dans l'image suivante :

Workflow de publication des événements.

Workflow transactionnel

Dans un workflow transactionnel, deux services communiquent entre eux via des événements. Dans cette architecture, la commande du client est traitée comme suit :

  1. Le client envoie une requête de commande qui spécifie l'ID du client et le nombre d'articles qu'il a commandés. La requête est envoyée au service Order via une API REST.
  2. Le service Order attribue un ID à la commande et stocke les informations sur celle-ci dans la base de données Order. L'état de la commande est marqué comme pending. Le service Order renvoie les informations de la commande au client et publie un événement qui les inclut dans le sujet Pub/Sub suivant : order-service-event.
  3. Le service Customer reçoit l'événement via une notification push. Elle augmente l'utilisation du crédit du client, qui est stockée dans la base de données Customer en fonction du nombre d'articles commandés.
  4. Si l'utilisation totale du crédit est inférieure ou égale à la limite prédéfinie, le service Customer publie un événement indiquant que l'augmentation du crédit a réussi. Dans le cas contraire, il publie un événement indiquant que l'augmentation du crédit a échoué. Dans ce cas, l'utilisation du crédit n'est pas augmentée.
  5. Le service Order reçoit l'événement via une notification push. Il remplace l'état de la commande par accepted ou rejected selon le cas. Le client peut suivre l'état de la commande à l'aide de l'ID de commande renvoyé par le service Order.

Le schéma suivant synthétise ce workflow :

Diagramme séquentiel du workflow Saga basé sur la chorégraphie.

Processus de publication des événements

Lorsqu'un microservice modifie ses propres données dans la base de données et publie un événement pour avertir la base de données, ces deux opérations doivent être effectuées de manière atomique. Par exemple, si le microservice échoue après qu'il a modifié des données sans publier d'événement, le processus transactionnel s'arrête. Dans ce cas, les données peuvent être laissées dans un état incohérent entre les différents microservices impliqués dans la transaction. Pour éviter ce problème, dans l'exemple d'application utilisé dans ce document, les microservices écrivent des données d'événement dans la base de données backend au lieu de publier directement les événements dans Pub/Sub.

Les données sont modifiées et les données d'événement associées sont écrites de manière atomique, à l'aide de la fonctionnalité transactionnelle de la base de données backend. Ce modèle, illustré dans l'image suivante, est couramment appelé "événements d'application" ou "boîte d'envoi transactionnelle".

Modèle utilisant une fonctionnalité transactionnelle.

Comme indiqué dans l'image précédente, la colonne published des données d'événement est initialement marquée comme False. Ensuite, le service event-publisher analyse régulièrement la base de données et publie les événements dont la colonne published contient la valeur False. Une fois l'événement publié, le service event-publisher remplace la valeur de la colonne published par True.

Comme le montre l'image suivante, la base de données Order et la base de données Event d'un même espace de noms peuvent être mises à jour de manière atomique par des transactions Datastore.

Mises à jour atomiques par des transactions Cloud Datastore.

Si le service event-publisher échoue après la publication d'un événement sans modifier la colonne published, il publie à nouveau le même événement après sa récupération. Étant donné que la nouvelle publication de l'événement entraîne un événement en double, les microservices qui reçoivent l'événement doivent rechercher des doublons potentiels et les gérer en conséquence. Cette approche permet de garantir l'idempotence de la gestion des événements.

L'image suivante montre comment un exemple d'application traite la duplication d'événements.

Un microservice met à jour sa base de données backend correspondante en fonction de la logique métier déclenchée par un événement.

Comme illustré dans le schéma précédent, l'application gère les événements en double avec le workflow suivant :

  • Chaque microservice met à jour sa base de données backend correspondante en fonction de la logique métier déclenchée par un événement, et écrit l'ID d'événement dans sa base de données.
  • Ces deux écritures sont effectuées de manière atomique à l'aide de la fonctionnalité transactionnelle utilisée par les bases de données backend.
  • Si les services reçoivent un événement en double, celui-ci est détecté lorsque les services recherchent l'ID d'événement dans leurs bases de données.

La gestion des événements en double est une pratique courante lors de la réception d'événements depuis Pub/Sub, car il y a une petite chance que Pub/Sub entraîne la diffusion de messages en double.

Développer l'architecture

Dans l'exemple d'application, avant de traiter un message, vous pouvez utiliser Datastore pour vérifier s'il est dupliqué. Cette approche signifie que le service qui utilise les messages (le service Customer, dans ce cas) est idempotent. Cette approche est généralement appelée "modèle consommateur idempotent". Certains frameworks mettent en œuvre ce modèle en tant que fonctionnalité intégrée, par exemple Eventuate.

Cependant, l'accès à la base de données à chaque fois que vous traitez un message peut entraîner des problèmes de performances. Une solution consiste à utiliser une base de données offrant de bonnes performances et une bonne évolutivité, par exemple Redis.

Modèle d'orchestration synchrone

Cette section explique comment mettre en œuvre un modèle de microservices d'orchestration synchrone dans un workflow transactionnel.

Présentation de l'architecture

Dans ce modèle, un seul orchestrateur contrôle le flux d'exécution d'une transaction. La communication entre les microservices et l'orchestrateur s'effectue de manière synchrone via des API REST.

Dans l'exemple d'architecture décrit dans ce document, Cloud Run est utilisé comme environnement d'exécution de microservices et Datastore comme base de données backend pour chaque service. En outre, Workflows est utilisé en tant qu'orchestrateur. Ce schéma est illustré dans l'image suivante :

Modèle avec Cloud Run en tant qu'environnement d'exécution de microservices et Datastore en tant que base de données backend pour chaque service.

Workflow transactionnel

Dans l'architecture d'un workflow synchrone, la commande d'un client est traitée comme suit :

  1. Le client envoie une requête de commande qui spécifie l'ID d'un client et le nombre d'articles qu'il a commandés. La requête est envoyée au service Order processor via l'API REST.
  2. Le service Order processor exécute un workflow dans lequel l'ID client et le nombre d'articles sont transmis à Workflows.
  3. Le workflow appelle l'API REST du service Order et transmet l'ID client et le nombre d'articles commandés par le client. Ensuite, le service Order attribue un ID de commande à la commande du client et stocke les informations concernant celle-ci dans la base de données Order. L'état de la commande est marqué comme pending. Le service Order renvoie les informations de la commande au workflow.
  4. Le workflow appelle l'API REST du service Customer et transmet l'ID client et le nombre d'articles commandés par le client. Ensuite, le service Customer augmente l'utilisation du crédit du client qui est stockée dans la base de données Customer en fonction du nombre d'articles commandés.
  5. Si l'utilisation totale du crédit est inférieure ou égale à la limite prédéfinie, le service Customer renvoie des données expliquant que l'augmentation du crédit a réussi. Dans le cas contraire, il renvoie des données expliquant que l'augmentation du crédit a échoué. Dans ce cas, l'utilisation du crédit n'est pas augmentée.
  6. Le workflow appelle l'API REST du service Order pour remplacer l'état de la commande par accepted ou rejected, selon le cas. Enfin, il renvoie les informations sur la commande dans la mise à jour de l'état final au service Order processor. Ensuite, le service Order processor renvoie ces informations au client.

Ce workflow est résumé dans le diagramme suivant :

Diagramme séquentiel du workflow d'orchestration synchrone.

Avantages et inconvénients

Lorsque vous envisagez de mettre en œuvre un modèle Saga basé sur la chorégraphie ou un modèle d'orchestration synchrone, le meilleur choix pour votre organisation est toujours le modèle le plus adapté à ses besoins. Toutefois, en général, en raison de sa simplicité de conception, l'orchestration synchrone est souvent le premier choix pour de nombreuses entreprises.

Le tableau suivant récapitule les avantages et les inconvénients du modèle Saga basé sur la chorégraphie et du modèle d'orchestration synchrone décrits dans ce document.


Avantages

Inconvénients

Saga basé sur la chorégraphie

Couplage faible : chaque service publie des événements dans Datastore en cas de modification de ses propres données. Aucune information n'est envoyée à d'autres services. Cette approche rend chaque service plus indépendant, et réduit le risque d'avoir besoin de modifier les services lorsque vous ajoutez de nouveaux services au workflow.

Dépendance complexe : la mise en œuvre de l'intégralité du workflow est répartie entre les services. Par conséquent, il peut être difficile de comprendre le workflow. Cette approche pourrait introduire accidentellement une complexité dans les modifications de conception et le dépannage futurs.

Orchestration synchrone

Dépendance simple : un seul orchestrateur contrôle l'intégralité du flux d'exécution d'une transaction. Il est ainsi plus facile de comprendre le fonctionnement du flux de transactions. Ce modèle simplifie la modification du workflow et le dépannage.

Risque de couplage fort : l'orchestrateur central dépend de tous les services qui composent le workflow transactionnel. Par conséquent, lorsque vous modifiez l'un de ces services ou ajoutez de nouveaux services au workflow, vous devrez peut-être modifier l'orchestrateur en conséquence. Cet effort supplémentaire peut surpasser l'avantage de pouvoir modifier et ajouter des services plus indépendamment de l'architecture de microservices par rapport aux systèmes monolithiques.

Étapes suivantes