Aprendendo a se comunicar entre microsserviços no Node.js através do RabbitMQ

Esta é uma continuação do artigo " Estamos escrevendo o primeiro microsserviço no Node.js com comunicação através do RabbitMQ ", que foi bem recebido pelos usuários do Habr.


Neste artigo, falarei sobre como se comunicar adequadamente entre microsserviços, para que eles permaneçam isolados.


Como não fazer


Por que você precisa se comunicar entre microsserviços? Use um banco de dados, leia a partir daí o que você quer - algo comercial!


Não, você não pode fazer isso. O conceito de microsserviços é que eles são isolados um do outro, ninguém sabe nada sobre ninguém (praticamente). Provavelmente, no futuro, quando o sistema começar a crescer, você desejará expandir a funcionalidade e precisará se comunicar entre microsserviços: por exemplo, um usuário comprou um produto, portanto, você deve enviar uma notificação sobre a venda ao vendedor.


Benefícios de isolamento


Confiabilidade


Suponha que haja um aplicativo monolítico no qual haja vários controladores:


  1. Produtos>
  2. Descontos
  3. O blog
  4. Utilizadores

Um belo dia, nosso banco de dados está caindo: agora não podemos obter produtos, sem descontos, sem postagens no blog, sem usuários. O site está completamente indisponível, os clientes não podem fazer login, os negócios estão perdendo lucros.


O que acontecerá na arquitetura de microsserviços?


Em outro universo, no mesmo dia em que o banco de dados de microsserviço do usuário cai, ele se torna inacessível: os usuários não podem sair, se registrar e fazer login. Parece que tudo está ruim e os negócios também estão perdendo lucro, mas não: compradores em potencial podem olhar para os produtos disponíveis, ler o blog da empresa e obter descontos.


Devido ao fato de que cada microsserviço possui seu próprio banco de dados, os efeitos colaterais se tornam muito menores.


Isso é chamado de degradação gradual .


Abstração


Em um aplicativo grande, é muito difícil se concentrar em uma tarefa, pois alterar um pequeno middleware pode quebrar algum tipo de controlador. Deseja usar o novo cliente para redis - não, o controlador que escrevemos há três anos usa a versão 0.1.0. Deseja finalmente tirar proveito dos novos recursos do Node.js. 10? Ou talvez 12? Desculpe, mas o monólito usa a versão 6.


Como se comunicar


Desde que começamos a falar sobre o exemplo "um usuário comprou um produto, enviamos um aviso de venda ao vendedor" e o implementamos.


O esquema é o seguinte:


  1. O usuário envia uma solicitação ao mercado de microsserviços para comprar itens no link / market / buy /: id
  2. A bandeira está gravada no banco de dados em que o produto é vendido
  3. No mercado de microsserviço, uma solicitação é enviada para as notificações de microsserviço, às quais os clientes estão conectados via WebSocket
  4. As notificações de microsserviço enviam uma mensagem ao vendedor sobre a venda de itens

Instale o MicroMQ


$ npm i micromq@1 -S 

Escrevendo um gateway


 const Gateway = require('micromq/gateway'); //   const gateway = new Gateway({ microservices: ['market'], rabbit: { url: process.env.RABBIT_URL, }, }); //        market gateway.post('/market/buy/:id', (req, res) => res.delegate('market')); //      gateway.listen(process.env.PORT); 

Nosso gateway consiste em apenas um ponto de extremidade, mas isso é suficiente por exemplo e treinamento.


Escrevendo notificações de microsserviço


 const MicroMQ = require('micromq'); const WebSocket = require('ws'); //   const app = new MicroMQ({ name: 'notifications', rabbit: { url: process.env.RABBIT_URL, }, }); //        const ws = new WebSocket.Server({ port: process.env.PORT, }); //     const clients = new Map(); //    ws.on('connection', (connection) => { //     connection.on('message', (message) => { //  ,       . //      try/catch,    json! const { event, data } = JSON.parse(message); //   'authorize'         if (event === 'authorize' && data.userId) { //         clients.set(data.userId, connection); } }); }); //       , //    ! ws.on('close', ...); //   notify,      app.action('notify', (meta) => { //      ,    400 if (!meta.userId || !meta.text) { return [400, { error: 'Bad data' }]; } //     const connection = clients.get(meta.userId); //     ,    404 if (!connection) { return [404, { error: 'User not found' }]; } //    connection.send(meta.text); //  200   return { ok: true }; }); //   app.start(); 

Aqui, aumentamos o servidor de soquete da web e o microsserviço ao mesmo tempo para receber solicitações dos soquetes da web e do RabbitMQ.


O esquema é o seguinte:


  1. Um usuário se conecta ao nosso servidor de soquete da web
  2. O usuário é autorizado enviando um evento de authorize com seu userId dentro
  3. Mantemos a conexão do usuário para que você possa enviar notificações a ele no futuro
  4. Um evento chega no RabbitMQ que você precisa enviar uma notificação ao usuário
  5. Verificando a validade dos dados recebidos
  6. Obter conexão do usuário
  7. Enviar notificação

Escrevendo um mercado de microsserviços


 const MicroMQ = require('micromq'); const { Items } = require('./api/mongodb'); //   const app = new MicroMQ({ name: 'market', rabbit: { url: process.env.RABBIT_URL, }, }); //      app.post('/market/buy/:id', async (req, res) => { const { id } = req.params; //      const item = await Items.findOne({ id, isSold: false }); //   ,  404 if (!item) { res.status(404).json({ error: 'Item not found', }); return; } //  ,    ,    await Items.updateOne({ id, }, { $set: { isSold: true, }, }); //     ,    req.app.ask('notifications', { server: { action: 'notify', meta: { userId: item.sellerId, text: JSON.stringify({ event: 'notification', data: { text: `Item #${id} was sold!`, }, }), }, }, }) //  ,      .catch(err => console.log('Cannot send message via notifications microservice', err)); //    ,     res.json({ ok: true, }); }); //   app.start(); 

O esquema é o seguinte:


  1. Recebemos uma solicitação do usuário para a compra de um item
  2. Estamos procurando o item com o ID correto e verifique se ele ainda não foi vendido
  3. Marcar item como vendido
  4. Enviamos uma notificação ao vendedor sobre a venda em segundo plano
  5. Respondemos ao cliente

Verifique


  1. Começamos 3 processos
  2. Enviaremos POST / market / buy / 1
  3. Recebemos a resposta { ok: true }
  4. O vendedor recebe uma notificação

 $ PORT=9000 node ./src/gateway.js $ PORT=9001 node ./src/notifications.js $ MONGODB_URL=mongodb://localhost:27017/my-super-microservice node ./src/market.js 


Source: https://habr.com/ru/post/pt447250/


All Articles