Repita depois de mim:
REST e eventos assíncronos não são alternativas. Eles são completamente ortogonais.
Você pode ter um, ou o outro, ou ambos, ou nenhum. São ferramentas totalmente diferentes para domínios de problemas completamente diferentes. De fato, a comunicação de solicitação-resposta de propósito geral é absolutamente capaz de ser assíncrona, orientada a eventos e tolerante a falhas .
Como um exemplo trivial, o protocolo AMQP envia mensagens através de uma conexão TCP. No TCP, todos os pacotes devem ser reconhecidos pelo destinatário . Se um remetente de um pacote não receber um ACK para esse pacote, ele continuará reenviando esse pacote até que seja ACK ou até que a camada de aplicativo "desista" e abandone a conexão. Este é claramente um modelo de solicitação-resposta não tolerante a falhas, porque cada "solicitação de envio de pacote" deve ter uma "resposta de confirmação de pacote" associada, e a falha em responder resulta na falha de toda a conexão. No entanto, o AMQP, um protocolo padronizado e amplamente adotado para mensagens tolerantes a falhas assíncronas, é comunicado através do TCP! O que da?
O conceito principal em jogo aqui é que o sistema de mensagens tolerante a falhas e escalável e com acoplamento fraco é definido por quais mensagens você envia , não por como as envia . Em outras palavras, o acoplamento flexível é definido na camada de aplicação .
Vejamos duas partes se comunicando diretamente com o RESTful HTTP ou indiretamente com um intermediário de mensagens AMQP. Suponha que a Parte A deseje fazer upload de uma imagem JPEG na Parte B, que irá afiar, compactar ou aprimorar a imagem. A Parte A não precisa da imagem processada imediatamente, mas exige uma referência a ela para uso e recuperação futuros. Aqui está uma maneira que pode ser usada no REST:
- A Parte A envia uma
POST
mensagem de solicitação HTTP para a Parte B comContent-Type: image/jpeg
- O Partido B processa a imagem (por muito tempo, se for grande) enquanto o Partido A espera, possivelmente fazendo outras coisas
- A parte B envia uma
201 Created
mensagem de resposta HTTP para a parte A com um Content-Location: <url>
cabeçalho vinculado à imagem processada
- A Parte A considera seu trabalho concluído, pois agora tem uma referência à imagem processada
- Em algum momento no futuro, quando a Parte A precisar da imagem processada, ela a obterá usando o link do
Content-Location
cabeçalho anterior
O 201 Created
código de resposta informa ao cliente que sua solicitação não apenas foi bem-sucedida, como também criou um novo recurso. Em uma resposta 201, o Content-Location
cabeçalho é um link para o recurso criado. Isso é especificado nas seções 6.3.2 e 3.1.4.2 da RFC 7231.
Agora, vamos ver como essa interação funciona em um protocolo RPC hipotético sobre o AMQP:
- A Parte A envia a um intermediário de mensagens AMQP (chame-a de Messenger) uma mensagem contendo a imagem e instruções para rotear para a Parte B para processamento e, em seguida, responda à Parte A com um endereço de algum tipo para a imagem
- O partido A espera, possivelmente fazendo outras coisas
- O Messenger envia a mensagem original do Partido A para o Partido B
- A parte B processa a mensagem
- A Parte B envia ao Messenger uma mensagem contendo um endereço para a imagem processada e instruções para rotear essa mensagem para a Parte A
- O Messenger envia à Parte A a mensagem da Parte B contendo o endereço da imagem processada
- A Parte A considera seu trabalho concluído, pois agora tem uma referência à imagem processada
- Em algum momento no futuro, quando a Parte A precisar da imagem, ela será recuperada usando o endereço (possivelmente enviando mensagens para outra parte)
Você vê o problema aqui? Em ambos os casos, Partido A não pode obter um endereço imagem até depois do partido B processa a imagem . No entanto, a Parte A não precisa da imagem imediatamente e, com todos os direitos, não se importava se o processamento ainda estivesse concluído!
Podemos corrigir isso com muita facilidade no caso AMQP, solicitando que a Parte B diga a A que B aceitou a imagem para processamento, fornecendo a A um endereço para onde a imagem estará após a conclusão do processamento. A Parte B pode enviar uma mensagem para A em algum momento no futuro, indicando que o processamento da imagem está concluído. Mensagens AMQP para o resgate!
Exceto adivinhe: você pode conseguir a mesma coisa com o REST . No exemplo do AMQP, alteramos uma mensagem "aqui está a imagem processada" para uma mensagem "a imagem está sendo processada, você pode obtê-la mais tarde". Para fazer isso no RESTful HTTP, usaremos o 202 Accepted
código Content-Location
novamente:
- A Parte A envia uma
POST
mensagem HTTP para a Parte B comContent-Type: image/jpeg
- A Parte B envia imediatamente uma
202 Accepted
resposta que contém algum tipo de conteúdo de "operação assíncrona" que descreve se o processamento está concluído e onde a imagem estará disponível quando terminar o processamento. Também está incluído um Content-Location: <link>
cabeçalho que, em uma 202 Accepted
resposta, é um link para o recurso representado por qualquer que seja o corpo da resposta. Nesse caso, isso significa que é um link para nossa operação assíncrona!
- A Parte A considera seu trabalho concluído, pois agora tem uma referência à imagem processada
- Em algum momento no futuro, quando a Parte A precisar da imagem processada, primeiro obtém o recurso de operação assíncrona vinculado ao
Content-Location
cabeçalho para determinar se o processamento foi concluído. Nesse caso, a Parte A usa o link na própria operação assíncrona para OBTER a imagem processada.
A única diferença aqui é que, no modelo AMQP, o Partido B informa ao Partido A quando o processamento da imagem é concluído. Porém, no modelo REST, a Parte A verifica se o processamento é realizado logo antes de realmente precisar da imagem. Essas abordagens são equivalentemente escaláveis . À medida que o sistema aumenta, o número de mensagens enviadas nas estratégias assíncrona AMQP e REST assíncrona aumenta com uma complexidade assintótica equivalente. A única diferença é que o cliente está enviando uma mensagem extra em vez do servidor.
Mas a abordagem REST tem mais alguns truques na manga: descoberta dinâmica e negociação de protocolo . Considere como as interações REST de sincronização e assíncrona começaram. A Parte A enviou exatamente o mesmo pedido à Parte B, com a única diferença sendo o tipo específico de mensagem de sucesso com a qual a Parte B respondeu. E se a Parte A quisesse escolher se o processamento da imagem era síncrono ou assíncrono? E se o Partido A não souber se o Partido B é capaz de processar assíncrono?
Bem, o HTTP já tem um protocolo padronizado para isso! Chama-se Preferências HTTP, especificamente a respond-async
preferência da Seção 4.1 da RFC 7240. Se a Parte A desejar uma resposta assíncrona, ela incluirá um Prefer: respond-async
cabeçalho com sua solicitação POST inicial. Se a Parte B decidir atender a essa solicitação, ela envia de volta uma 202 Accepted
resposta que inclui a Preference-Applied: respond-async
. Caso contrário, o Partido B simplesmente ignora o Prefer
cabeçalho e envia de volta 201 Created
como faria normalmente.
Isso permite que a Parte A negocie com o servidor, adaptando-se dinamicamente a qualquer implementação de processamento de imagem com a qual estiver falando. Além disso, o uso de links explícitos significa que a Parte A não precisa saber sobre nenhuma outra parte além de B: nenhum intermediário de mensagens AMQP, nenhum misterioso Parte C que sabe como transformar o endereço da imagem em dados de imagem, nenhum segundo B-Async participe se for necessário fazer solicitações síncronas e assíncronas etc. Ele simplesmente descreve o que precisa, do que seria opcional e, em seguida, reage aos códigos de status, ao conteúdo da resposta e aos links. Adicionar emCache-Control
cabeçalhos para obter instruções explícitas sobre quando manter cópias locais de dados e agora os servidores podem negociar com os clientes quais recursos os clientes podem manter cópias locais (ou até offline!). É assim que você cria microsserviços tolerantes a falhas com pouco acoplamento no REST.