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â!
Commençons avec un exemple simple et direct : lâappel et la rĂ©ponse.
Le consommateur formule une requĂȘte : "Puis-je avoir un verre dâeauâ?".
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 :
la requĂȘte est formulĂ©e,
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.
L'approche asynchrone permet de ne pas subir cette latence justement !
Le consommateur peut :
formuler une requĂȘte,
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. đ
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".
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 :
via un back-office de la solution, vous indiquerez une URL de rappel
lorsque vous requĂȘterez la solution, vous transmettrez un identifiant de paiement
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 !
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.
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â !