Répète après moi:
Les événements REST et asynchrones ne sont pas des alternatives. Ils sont complètement orthogonaux.
Vous pouvez avoir l'un, ou l'autre, ou les deux, ou aucun. Ce sont des outils entièrement différents pour des domaines de problèmes entièrement différents. En fait, la communication demande-réponse à usage général est absolument capable d'être asynchrone, déclenchée par les événements et tolérante aux pannes .
À titre d'exemple trivial, le protocole AMQP envoie des messages via une connexion TCP. Dans TCP, chaque paquet doit être reconnu par le récepteur . Si l'expéditeur d'un paquet ne reçoit pas un ACK pour ce paquet, il continue de renvoyer ce paquet jusqu'à ce qu'il soit ACK ou jusqu'à ce que la couche application "abandonne" et abandonne la connexion. Il s'agit clairement d'un modèle de demande-réponse non tolérant aux pannes car chaque "demande d'envoi de paquet" doit avoir une "réponse d'accusé de réception de paquet" qui l'accompagne, et la non-réponse entraîne l'échec de toute la connexion. Pourtant, AMQP, un protocole normalisé et largement adopté pour la messagerie asynchrone à tolérance de panne, est communiqué via TCP! Ce qui donne?
Le concept de base en jeu ici est que la messagerie évolutive à tolérance de pannes faiblement couplée est définie par les messages que vous envoyez , et non par la façon dont vous les envoyez . En d'autres termes, un couplage lâche est défini au niveau de la couche d'application .
Regardons deux parties communiquant soit directement avec HTTP RESTful, soit indirectement avec un courtier de messages AMQP. Supposons que la partie A souhaite télécharger une image JPEG vers la partie B qui accentuera, compressera ou améliorera l'image. La partie A n'a pas besoin de l'image traitée immédiatement, mais nécessite une référence à celle-ci pour une utilisation et une récupération futures. Voici une façon de procéder dans REST:
- La partie A envoie un
POST
message de demande HTTP à la partie B avecContent-Type: image/jpeg
- La partie B traite l'image (pendant longtemps si elle est grande) pendant que la partie A attend, peut-être en faisant autre chose
- La partie B envoie un
201 Created
message de réponse HTTP à la partie A avec un en- Content-Location: <url>
tête qui renvoie à l'image traitée
- La partie A considère que son travail est effectué car elle a désormais une référence à l'image traitée
- Dans le futur, lorsque la partie A a besoin de l'image traitée, elle l'obtient en utilisant le lien de l'en-
Content-Location
tête précédent
Le 201 Created
code de réponse indique à un client que non seulement sa demande a réussi, il a également créé une nouvelle ressource. Dans une réponse 201, l'en- Content-Location
tête est un lien vers la ressource créée. Ceci est spécifié dans les sections 6.3.2 et 3.1.4.2 de la RFC 7231.
Maintenant, voyons comment cette interaction fonctionne sur un hypothétique protocole RPC au-dessus d'AMQP:
- La partie A envoie à un courtier de messages AMQP (appelez-le Messenger) un message contenant l'image et des instructions pour la router vers la partie B pour traitement, puis répondez à la partie A avec une adresse quelconque pour l'image
- La partie A attend, peut-être en faisant autre chose
- Messenger envoie le message original de la partie A à la partie B
- La partie B traite le message
- La partie B envoie à Messenger un message contenant une adresse pour l'image traitée et des instructions pour acheminer ce message vers la partie A
- Messenger envoie à la partie A le message de la partie B contenant l'adresse de l'image traitée
- La partie A considère que son travail est effectué car elle a désormais une référence à l'image traitée
- Dans le futur, lorsque la partie A a besoin de l'image, elle récupère l'image en utilisant l'adresse (éventuellement en envoyant des messages à une autre partie)
Voyez-vous le problème ici? Dans les deux cas, la partie A ne peut obtenir une adresse d'image qu'après que la partie B a traité l'image . Pourtant, la partie A n'a pas besoin de l'image tout de suite et, par tous les droits, ne s'en soucie pas si le traitement est encore terminé!
Nous pouvons résoudre ce problème assez facilement dans le cas AMQP en demandant à la partie B de dire à A que B a accepté l'image pour le traitement, en donnant à A une adresse indiquant où l'image se trouvera une fois le traitement terminé. La Partie B peut alors envoyer à A un message dans le futur indiquant que le traitement d'image est terminé. La messagerie AMQP à la rescousse!
Sauf devinez quoi: vous pouvez réaliser la même chose avec REST . Dans l'exemple AMQP, nous avons changé un message "voici l'image traitée" en un message "l'image est en cours de traitement, vous pouvez l'obtenir plus tard". Pour ce faire dans HTTP RESTful, nous utiliserons le 202 Accepted
code et Content-Location
encore:
- La partie A envoie un
POST
message HTTP à la partie B avecContent-Type: image/jpeg
- La partie B renvoie immédiatement une
202 Accepted
réponse qui contient une sorte de contenu "d'opération asynchrone" qui décrit si le traitement est terminé et où l'image sera disponible une fois le traitement terminé. Un en- Content-Location: <link>
tête est également inclus qui, dans une 202 Accepted
réponse, est un lien vers la ressource représentée par le corps de la réponse. Dans ce cas, cela signifie que c'est un lien vers notre fonctionnement asynchrone!
- La partie A considère que son travail est effectué car elle a désormais une référence à l'image traitée
- Dans le futur, lorsque la partie A a besoin de l'image traitée, elle obtient d'abord la ressource d'opération asynchrone liée à l'en-
Content-Location
tête pour déterminer si le traitement est terminé. Si c'est le cas, la partie A utilise ensuite le lien dans l'opération asynchrone elle-même pour obtenir l'image traitée.
La seule différence ici est que dans le modèle AMQP, la partie B indique à la partie A lorsque le traitement d'image est terminé. Mais dans le modèle REST, la partie A vérifie si le traitement est effectué juste avant d'avoir réellement besoin de l'image. Ces approches sont évolutives de manière équivalente . À mesure que le système s'agrandit, le nombre de messages envoyés dans les stratégies async AMQP et async REST augmente avec une complexité asymptotique équivalente. La seule différence est que le client envoie un message supplémentaire au lieu du serveur.
Mais l'approche REST a encore quelques trucs dans sa manche: découverte dynamique et négociation de protocole . Considérez comment les interactions REST de synchronisation et d'async ont commencé. La Partie A a envoyé exactement la même demande à la Partie B, la seule différence étant le type particulier de message de réussite auquel la Partie B a répondu. Et si la partie A voulait choisir si le traitement d'image était synchrone ou asynchrone? Que se passe-t-il si la partie A ne sait pas si la partie B est même capable d'un traitement asynchrone?
Eh bien, HTTP a déjà un protocole standardisé pour cela! C'est ce qu'on appelle les préférences HTTP, en particulier la respond-async
préférence de la RFC 7240 Section 4.1. Si la partie A souhaite une réponse asynchrone, elle inclut un en- Prefer: respond-async
tête avec sa demande POST initiale. Si la partie B décide d'honorer cette demande, elle renvoie une 202 Accepted
réponse qui comprend a Preference-Applied: respond-async
. Sinon, la partie B ignore simplement l'en- Prefer
tête et renvoie 201 Created
comme d'habitude.
Cela permet à la partie A de négocier avec le serveur, en s'adaptant dynamiquement à l'implémentation du traitement d'image avec laquelle elle se trouve. De plus, l'utilisation de liens explicites signifie que la partie A n'a pas à connaître d'autres parties que B: pas de courtier de messages AMQP, pas de mystérieuse partie C qui sait comment transformer réellement l'adresse d'image en données d'image, pas de deuxième B-Async partie si des demandes synchrones et asynchrones doivent être effectuées, etc. Il décrit simplement ce dont il a besoin, ce qu'il aimerait éventuellement, puis réagit aux codes d'état, au contenu des réponses et aux liens. Ajouter àCache-Control
en-têtes pour des instructions explicites sur le moment de conserver des copies locales des données, et maintenant les serveurs peuvent négocier avec les clients les ressources que les clients peuvent conserver des copies locales (ou même hors ligne!) de. C'est ainsi que vous créez des microservices à tolérance de panne à couplage lâche dans REST.