Utilizando o conceito RPC com NodeJS e RabbitMQ

Introdução

Neste artigo abordaremos o conceito RPC utilizando NodeJS e RabbitMQ, entenderemos cada uma dessas palavrinhas e vamos aplicar este conceito em um cenário real.

Começando

Veremos na prática como implementar um client com NodeJS e fazê-lo comunicar-se com o server por meio de um message broker chamado RabbitMQ, aplicando assim o conceito RPC. É necessário que você tenha como pré-requisito conhecimento básico em Javascript e NodeJS.

Para dar início a implementação sobre o conceito RPC precisamos entender primeiro as tecnologias que utilizaremos neste artigo, que são elas:

NodeJS

NodeJS é uma das maiores criações da tecnologia que já se existiu, é uma plataforma que podemos escrever javascript para front-end e back-end, automatizar processos, desenvolver APIs, realizar comunicação em tempo real e muito mais, tudo isso com apenas uma linguagem, Javascript.

NodeJS é leve e multiplataforma e foi criado por Ryan Dahl em 2009 e seu funcionamento gira em torno do motor V8, o mesmo utilizado no navegador do Google Chrome.

RabbitMQ

RabbitMQ é um Message Broker que foi criado em 2007 pela Rabbit Technologies, desenvolvido na linguagem Erlang e tem suporte ao protocolo de mensagens AMQP(Advanced Message Queuing Protocol) suportando troca de mensagens assíncronas. Além de ser multiplataforma é compatível com diversas linguagens de programação, é considerado rápido, confiável e adequado em cenários de alta disponibilidade.

Se fizermos uma rápida analogia, podemos dizer que o RabbitMQ é uma caixa postal, uma agência postal e um carteiro, recepcionando cartas(mensagens) e encaminhando a seus destinatários.

Antes de continuarmos vamos entender alguns conceitos dos jargões utilizados no RabbitMQ:

Producer – É um publicador de mensagens


Queue – É caixa postal – Local onde são armazenadas as mensagens

 
Consumer – Espera e recebe as mensagens

RPC

A sigla RPC é um acrônimo de Remote Procedure Call(Chamada de Procedimento Remoto), sendo um padrão de comunicação que nos ajuda a executar funções em um computador remoto e aguardar o seu resultado, é como se chamássemos uma função da nossa aplicação, mas essa função está em outro computador. RPC facilita a comunicação entre client/server e veremos na prática como realizar a comunicação do nosso client rodando com NodeJS, que enviará uma requisição, passando por um message broker e aguardando a resposta processada pelo server.

O padrão RPC permite uma arquitetura resiliente já que estamos utilizando filas para armazenamento de mensagens e garantimos o processamento de eventos entre client e server.

Cenário Proposto


Imaginemos que estamos no período mais movimentado do comércio eletrônico: Black Friday.

Desenvolveremos uma aplicação que fará requisições em uma API, mas mesmo que o servidor esteja indisponível as requisições não serão perdidas e serão processadas posteriormente.

Na imagem acima temos uma ilustração simples e sem problemas de um client enviando requisições para o server, a comunicação é feita via http e o server tem se mantido estável em seu processamento e retorna a resposta para o client sem problemas.

Mas agora entramos na semana da Black Friday e o server está sendo requisitado por diversos clients. Começou a apresentar problemas ao processar as requisições, dependendo da gravidade do problema poderá ficar indisponível por um pequeno, médio ou longo período de tempo.

Neste cenário podemos considerar que todas as requisições estão perdidas, mas e agora ? Como podemos contornar este cenário ?

Solução

Umas das soluções é utilizarmos chamadas RPC entre client e server, com este padrão de comunicação implementado com RabbitMQ, conseguiremos garantir que as requisições serão processadas após o servidor retornar à sua disponibilidade.

Neste artigo utilizaremos Windows, mas em Linux e MacOS os comandos são os mesmos.

Show me the code

Faça o clone do projeto de exemplo com o seguinte comando:

 git clone https://github.com/fsvfernando/wevo-nodejs-rpc-rabbitmq.git

Faça a instalação dos pacotes

 npm install

Você precisará também ter instalado o RabbitMQ, mas para você não enfrentar todo esse processo de instalação, você poderá utilizar um serviço on-line do RabbitMQ que permite utilizar um plano gratuito(possui limitações), basta se registrar e usar, o nosso exemplo usa essa plataforma, então, acesse e siga os passos:

https://www.cloudamqp.com/

Vamos entender na prática como podemos implementar chamadas RPC com Javascript e o message broker RabbitMQ.

Neste diagrama podemos entender que:

  • Quando o client é iniciado é criada uma fila de callback, exclusiva e anônima, onde o server irá retornar o resultado para o client. A fila é o local onde será armazenada a mensagem e posteriormente consumida pelo Server.
  • Para uma chamada RPC é necessário que o client envie duas propriedades importantes:
    • reply_to – nome da fila de retorno da chamada callback.
    • correlation_id – identificador único e exclusivo da solicitação.
  • Quando o client envia a solicitação para o server ele publica a mensagem em uma fila chamada rpc_queue.
  • O server está aguardando solicitações na fila rpc_queue. Quando aparecer uma nova solicitação, então, ele faz o trabalho de processamento e publica o resultado de volta para o client usando a fila de retorno definida pelo client na propriedade reply_to.
  • E por fim, o client aguarda os dados de retorno e quando a mensagem aparece ele verifica se a mensagem que chegou é a que ele esperava e isso ele identifica pela propriedade correlation_id.

Parece difícil ? Mas fique tranquilo que não é, vamos ver isso na prática como ficaria. 

A nossa aplicação de exemplo, fará uma simulação de processamento de números primos, o client envia um número primo e o server recebe essa informação e processá,WE identificando se o número é primo ou não e então, retorna o resultado para o client.

Esse exemplo é simples, mas você poderá utilizar deste conceito para muitos outros cenários, então vamos lá.

Rode o Server, para isso abra uma instância de terminal na pasta raiz do projeto clonado e digite o seguinte comando:

node server.js

Já para rodar o Client, abra uma nova instância do terminal e digite o seguinte comando: 

node client.js 9

O número 9 é um parâmetro e você poderá alterar esse parâmetro passando qualquer número que você queira descobrir se é um número primo ou não.

Server – Entendendo a implementação do lado do Server

Criamos a conexão e o canal de comunicação e declaramos a fila que recepciona as mensagens:

 amqp.connect(config.rabbitMQConnectionString, (err, conn) => {
  conn.createChannel((err, ch) => {
    ch.assertQueue(config.rpcQueue, { durable: false });
    ch.prefetch(1);


O próximo trecho de código consome e processa as mensagens que chegam:

    ch.consume(config.rpcQueue, function reply(msg) {
      const num = parseInt(msg.content.toString(), 10);

Por fim, enviamos a resposta após a mesma ser processada para a fila de callback:

      let optionsPublish = { 
        correlationId: msg.properties.correlationId 
      };
      ch.sendToQueue(
          msg.properties.replyTo,
          new Buffer(`Result: ${msgReturn}`),
          optionsPublish
      );

      ch.ack(msg);

Client – Entendendo a implementação do lado do client.

Criamos a conexão e o canal de comunicação e depois, criamos a fila de callback para recepcionar as mensagens de resposta:

 
  amqp.connect(config.rabbitMQConnectionString, (err, conn) => {
     conn.createChannel((err, ch) => {
     ch.assertQueue('', { exclusive: true }, (err, q) => {

Geramos o correlation_id para a mensagem que será enviada e enviamos para a fila a qual o Server está ouvindo as mensagens.

    const corr = uuid();
    ch.sendToQueue(config.rpcQueue,
      new Buffer(numero.toString()),
      { correlationId: corr, replyTo: q.queue });

E por fim, obtemos a resposta pela fila de callback:

    ch.consume(q.queue, (msg) => {
      if (msg.properties.correlationId === corr) 
      {
        console.log(` [.] Got ${msg.content.toString()}`);
        setTimeout(function() { conn.close(); process.exit(0) }, 500);
      }
    }, {noAck: true});

Conclusão

Vemos agora que temos uma arquitetura mais resiliente, inserimos um message broker que armazena mensagens enviadas pelo client e posteriormente consumidas e processadas pelo server.

Garantimos que mesmo que o server fique indisponível por algum tempo as requisições serão armazenadas no message broker e assim que houver disponibilidade novamente do server, as mensagens serão processadas e devolvidas a seus clients.

Fizemos tudo isso utilizando apenas Javascript com uma implementação para comunicação entre client e server com RabbitMQ e utilizando o padrão RPC.

Espero que tenham gostado. Acesse o código fonte no meu repositório no Github.

Um abraço.

Referências

NodeJS: nodejs.org/

RabbitMQ: www.rabbitmq.com

RPC: en.wikipedia.org/wiki/Remote_procedure_call


Fernando de Souza Vieira

Líder Técnico do time de Produtos da WevoLinkedin: www.linkedin.com/in/fernandosouzavieira

Conheça mais histórias

Confira outros cases, depoimentos e artigos de quem usa a tecnologia Wevo.

DIA consegue integrar o e-commerce com os sistemas internos 3 vezes mais rápida e com 80% de redução de custo

DPK integra todos seus sistemas em um novo e-commerce em apenas 6 semanas utilizando a tecnologia e expertise da Wevo

LF Máquinas integra e-commerce com seu ERP próprio em tempo recorde utilizando a plataforma da Wevo