• 8 heures
  • Moyenne

Ce cours est visible gratuitement en ligne.

course.header.alt.is_video

course.header.alt.is_certifying

J'ai tout compris !

Mis à jour le 20/08/2024

Choisissez le bon style de communication pour votre application

Dans notre application Air Business, la communication entre les couches est simple et directe : nous utilisons des appels de fonction Java internes.

Cela dit, si nous étendons cette application pour qu’elle soit exécutée sur plusieurs serveurs (par exemple, si la partie base de données était exécutée sur un ou des serveurs dédiés), quelles options de communication s’offriraient à nous ?

L’envoi de messages d’une couche à l’autre peut prendre plusieurs formes. Voici quelques options de communication populaires :

  • Synchrone

  • Asynchrone

  • Polling (sondage)

  • Callback (rappel)

  • Publish-subscribe / Message queuing (publication-souscription/file d’attente de messages)

Voyons quels sont les avantages et les inconvénients de chacune de ces options, et comment les utiliser.

Mais avant de nous lancer, voici deux termes que vous aurez besoin de connaître :

  • Consumer/Consommateur : C’est ce qui formule la requête. Si vous allez au restaurant et que vous passez votre commande, vous êtes le consommateur.

  • Producer ou supplier/producteur ou fournisseur : C’est ce qui répond à la requête du consommateur. Dans notre exemple du restaurant, c’est la personne chargée du service.

Un fournisseur dans une situation donnée peut être un consommateur dans une autre situation. Par exemple, une fois que la personne chargée du service a pris notre commande, cette dernière est envoyée en cuisine pour être préparée. L’envoi de ce message à la cuisine transforme notre serveur ou serveuse en consommateur pour cette partie du processus du repas.

Tout est clair ? Alors, lançons-nous !

Comprenez le principe de l'approche synchrone

Commençons avec un exemple simple et direct : l’appel et la réponse.

  1. Le consommateur formule une requête : "Puis-je avoir un verre d’eau ?".

  2. Le fournisseur accomplit ce qui est demandé : "Voici votre verre d’eau".

Le consommateur ne peut rien faire jusqu’à ce que le fournisseur réponde à sa requête : il ne peut pas boire d’eau jusqu’à ce que l’eau soit amenée à table.

C’est ainsi qu’Air Business est implémenté actuellement. Tous les appels de fonctions internes fonctionnent de cette façon. Et tous nos endpoints externes également.

Cette approche a l'avantage d'être très simple :

  1. la requête est formulée,

  2. et le consommateur attend la réponse.

Mais que se passe-t-il si la requête est mal formulée ? qu'elle comporte des erreurs ? Le consommateur pourrait attendre longtemps si nous n’envoyons pas de réponse d’erreur…

Cette latence représente le plus gros inconvénient de cette approche : le consommateur ne peut rien faire d’autre tant que la requête est en cours de traitement.

Découvrez les options asynchrones

L'approche asynchrone permet de ne pas subir cette latence justement !

Le consommateur peut :

  1. formuler une requête,

  2. puis continuer à faire autre chose en attendant.

Pour se faire, en Java, nous pouvons lancer des requêtes dans des threads différents.

Pour notre petite application, cela ne représente peut-être pas d'intérêt aujourd'hui car notre thread client "n’a rien d’autre à faire" en attendant la réponse à une requête. Mais que se passera-t-il quand nous déploierons l'application sur un serveur web et que nous recevrons des multitudes de requêtes simultanées ?

À ce moment-là, l’exploration d’une approche multi-threads pour gérer toutes les requêtes entrantes sera justifiée. Spring Boot effectue cela en coulisses. Chaque requête se voit attribuer son propre thread. C'est donc en fait du synchrone, mais par requête. 😄

Optez pour la technique du "polling"

Néanmoins, si le consommateur fait d’autres choses au lieu d’attendre, nous avons un problème.

Comment le fournisseur indique-t-il que le résultat est maintenant prêt pour le consommateur ? Nous devons introduire davantage de coordination entre le consommateur et le fournisseur.

Dans cette situation, le consommateur continue à demander au fournisseur si la réponse est prête. Un appel séparé est passé à l’API pour voir comment ça se passe.

Si vous avez déjà été en voiture avec un enfant, vous connaissez la chanson : « Quand est-ce qu'on arrive ? ». Une question répétée inlassablement. 😅

C’est une option facile à implémenter. La plus grande partie de la coordination est du côté du consommateur. Il continue simplement à demander si le fournisseur a terminé. Là où ça se complique, c’est si plusieurs consommateurs formulent des requêtes similaires. Le fournisseur doit donner un identifiant ou un token au consommateur. Le consommateur renvoie ensuite ce token au fournisseur, afin qu’il voie si la requête est achevée.

Ce fonctionnement ressemble un peu à celui d’un vestiaire dans un théâtre : vous donnez votre manteau et recevez en échange une carte portant un numéro. À la fin du spectacle, lorsque vous rendez la carte, elle permet à la personne du vestiaire de savoir quel manteau vous voulez.

L'inconvénient de cette approche, ce sont les ressources. On peut avoir beaucoup de demandes pour savoir si la réponse est prête, avant que la réponse ne le soit vraiment. Chaque question (et réponse) nécessite de la bande passante (réseau, processeur, etc.) des deux côtés de l’interaction. Alors, comment limiter le nombre d’interactions ? Avec la méthode du "callback".

Optez pour la technique du "callback"

Le plus efficace dans ce cas-là c'est de stopper les questions du consommateur.

Lorsque le fournisseur a terminé, le consommateur est rappelé. Seuls deux messages sont envoyés (la requête, la réponse), ce qui réduit considérablement la bande passante.

Dans notre approche d’endpoint, l’endpoint du consommateur peut être envoyé dans le message JSON. Le fournisseur doit sauvegarder ce endpoint, afin de savoir où appeler lorsque la réponse est prête.

De plus, le consommateur n’a aucune idée de quand le fournisseur enverra le résultat. Le consommateur devra dédier une ressource (un thread, par exemple) pour attendre et guetter la réponse.

De nombreuses requêtes peuvent avoir été envoyées au fournisseur par le consommateur. Lorsque le résultat arrive, le consommateur doit savoir à quelle requête le résultat correspond. Dans ce cas aussi, on utilise une approche avec un identifiant ou un token.

SI vous intégrez un jour une solution de paiement en ligne, vous utiliserez très probablement le callback :

  1. via un back-office de la solution, vous indiquerez une URL de rappel

  2. lorsque vous requêterez la solution, vous transmettrez un identifiant de paiement

  3. quand le paiement sera traité par la banque, la solution rappellera votre application à l’URL fournie, en vous renvoyant aussi l'identifiant de paiement

Un seul appel en requête, un seul appel en réponse !

Optez pour la technique du "publish/subscribe" ou "message queuing"

Et si de nombreux consommateurs s’intéressent au résultat de la réponse d’un fournisseur à un autre consommateur ? Ou vice versa ?

Un consommateur soumet une requête, mais ne s’intéresse pas à l’identité du fournisseur qui y répond, pourvu qu’elle soit gérée facilement. Peu m’importe quel est le cuisinier qui prépare mon repas, par exemple. C’est celui qui est libre qui devrait s’en occuper.

Avec cette approche, il y a une file d’attente ou un porteur central de toutes les requêtes. Et de la même façon : il y a une file d’attente ou un porteur de toutes les réponses. Un contrôleur s’assure que tous les messages (requêtes et réponses) vont là où ils doivent aller.

Le grand avantage de cette approche, c’est l’efficacité. Les messages ne sont envoyés qu’en cas de besoin. Et ils sont seulement stockés le temps nécessaire.

Dans l’entreprise de données médicales pour laquelle je travaillais, nous avions plusieurs files de publications/souscriptions. Certaines étaient configurées pour traiter les choses en quelques minutes, et supprimer les messages envoyés immédiatement. D’autres étaient configurées pour sauvegarder les messages pendant un mois.

Pour l’application Air Business, ces mécanismes vont au-delà de notre besoin. En revanche, si les demandes de réservations "s’envolaient" réellement, nous pourrions utiliser cette approche pour aider à répartir la charge sur plusieurs serveurs.

En résumé

  • Il existe de nombreuses options pour communiquer entre les couches, dont la plupart utilisent des consommateurs et des fournisseurs.

  • Les appels en synchrone à un fournisseur peuvent être implémentés avec REST.

  • Les mécanismes asynchrones doivent être utilisés si le temps de traitement par le fournisseur est long.

  • Le polling est facile à implémenter, mais peut être lent.

  • Les callbacks sont efficaces.

  • Les files d’attente de messages, ou publications/souscriptions, sont très efficaces, mais nécessitent généralement une implémentation tierce pour gérer tous les détails.

Maintenant que vous maîtrisez la communication, concluons ce cours  !

Exemple de certificat de réussite
Exemple de certificat de réussite