Aprendiendo a comunicarse entre microservicios en Node.js a través de RabbitMQ

Esta es una continuación del artículo " Estamos escribiendo el primer microservicio en Node.js con comunicación a través de RabbitMQ ", que fue bien recibido por los usuarios de Habr.


En este artículo hablaré sobre cómo comunicarse adecuadamente entre microservicios para que los microservicios permanezcan aislados.


Como no hacerlo


¿Por qué necesitas comunicarte entre microservicios? Use una base de datos, lea desde allí lo que quiera: ¡algo comercial!


No, no puedes hacer eso. El concepto de microservicios es que están aislados unos de otros, nadie sabe nada de nadie (prácticamente). Lo más probable es que, en el futuro, cuando el sistema comience a crecer, desee expandir la funcionalidad y necesite comunicarse entre microservicios: por ejemplo, un usuario compró un producto, por lo que debe enviar una notificación sobre la venta al vendedor.


Beneficios de aislamiento


Fiabilidad


Supongamos que hay una aplicación monolítica en la que hay varios controladores:


  1. Productos
  2. Descuentos
  3. El blog
  4. Los usuarios

Un buen día, nuestra base de datos está cayendo: ahora no podemos obtener ningún producto, sin descuentos, sin publicaciones de blog, sin usuarios. El sitio no está disponible por completo, los clientes no pueden iniciar sesión, las empresas están perdiendo ganancias.


¿Qué pasará en la arquitectura de microservicios?


En otro universo, el mismo día que cae la base de datos de microservicios del usuario, se vuelve inaccesible: los usuarios no pueden cerrar sesión, registrarse e iniciar sesión. Parece que todo está mal y el negocio también está perdiendo ganancias, pero no: los compradores potenciales pueden ver los productos disponibles, leer el blog de la compañía y encontrar descuentos.


Debido al hecho de que cada microservicio tiene su propia base de datos, los efectos secundarios son mucho menores.


Esto se llama degradación gradual .


Abstracción


En una aplicación grande, es muy difícil concentrarse en una tarea, ya que cambiar algunos middleware pequeños puede romper algún tipo de controlador. Quiere usar el nuevo cliente para redis: no, no puede, ese controlador que escribimos hace tres años usa la versión 0.1.0. ¿Desea finalmente aprovechar las nuevas características de Node.js 10? O tal vez 12? Lo sentimos, pero el monolito usa la versión 6.


Como comunicarse


Como comenzamos a hablar sobre el ejemplo "un usuario compró un producto, enviamos un aviso de venta al vendedor", y luego lo implementaremos.


El esquema es el siguiente:


  1. El usuario envía una solicitud al mercado de microservicios para comprar cosas en el enlace / market / buy /: id
  2. La bandera está escrita en la base de datos donde se vende el producto.
  3. Desde el mercado de microservicios, se envía una solicitud a las notificaciones de microservicios, a las cuales los clientes se conectan a través de WebSocket
  4. Las notificaciones de microservicio envían un mensaje al vendedor sobre la venta de cosas

Instalar MicroMQ


$ npm i micromq@1 -S 

Escribir un portal


 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); 

Nuestra puerta de enlace consta de un solo punto final, pero esto es suficiente por ejemplo y capacitación.


Escribir notificaciones de microservicio


 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(); 

Aquí elevamos el servidor de sockets web y el microservicio al mismo tiempo para recibir solicitudes tanto de sockets web como de RabbitMQ.


El esquema es el siguiente:


  1. Un usuario se conecta a nuestro servidor de socket web
  2. El usuario está autorizado enviando un evento de authorize con su ID de usuario dentro
  3. Mantenemos la conexión del usuario para que pueda enviarle notificaciones en el futuro.
  4. Llega un evento a RabbitMQ que necesita enviar una notificación al usuario
  5. Comprobación de la validez de los datos entrantes
  6. Obtener conexión de usuario
  7. Enviar notificación

Escribir un mercado de microservicios


 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(); 

El esquema es el siguiente:


  1. Recibimos una solicitud del usuario para la compra de un artículo.
  2. Estamos buscando el artículo con la identificación correcta y nos aseguramos de que aún no se haya vendido.
  3. Marcar artículo como vendido
  4. Enviamos una notificación al vendedor sobre la venta en segundo plano.
  5. Respondemos al cliente

Cheque


  1. Iniciamos 3 procesos
  2. Enviaremos POST / market / buy / 1
  3. Recibimos la respuesta { ok: true }
  4. El vendedor recibe una notificación

 $ 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/447250/


All Articles