📜 ⬆️ ⬇️

Learning to communicate between microservices on Node.js via RabbitMQ

This is a continuation of the article “ Writing the first microservice on Node.js with communication via RabbitMQ ”, which was well received by users of the habr.


In this article I will talk about how to properly communicate between microservices so that microservices remain isolated.


How not to do


Why do I need to communicate between microservices? You use one database, read what you want from there - business!


No, you can't do that. The concept of microservices is that they are isolated from each other, no one knows anything about anyone (practically). Most likely, in the future, when the system starts to grow, you will want to expand the functionality and you will need to communicate between microservices: for example, the user has bought the product, which means you need to send a notice of sale to the seller.


Insulation benefits


Reliability


Suppose there is a monolithic application in which there are several controllers:


  1. Products
  2. Discounts
  3. Blog
  4. Users

One day our database is falling: now we can not get any products, discounts, blog articles or users. The site is completely unavailable, customers can not enter, the business is losing profits.


What will happen in microservice architecture?


In another universe, on the same day, the microservice user database falls, it becomes inaccessible: users cannot log out, register and log in. It would seem that everything is bad and the business also loses profit, but no: potential buyers can look at the available products, read the company's blog, find discounts.


Due to the fact that each microservice has its own database, side effects become much smaller.


This is called gradual degradation .


Abstraction


In a large application, it is very difficult to focus on one task, because by changing some small middleware, you can break down some kind of controller. Want to use the new client for redis - no, you can't, the controller that we wrote three years ago uses version 0.1.0. Want to finally use the new features of Node.js 10? Or maybe 12? Sorry, but version 6 is used in the monolith.


How to communicate


Since we started talking about the example of “a user bought a product, we send a notice of sale to the seller,” then we implement it.


The scheme is as follows:


  1. The user sends a request to the market microservice for the purchase of things under the link / market / buy /: id
  2. The base is written flag that the goods are sold
  3. From microservice market a request is sent to microservice notifications, to which clients are connected via WebSocket
  4. Microservices notifications sends a message to the seller about the sale of things

Install MicroMQ


$ npm i micromq@1 -S 

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

A gateway in our country consists of only one endpoint, but this is enough for example and training.


We write microservices notifications


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

Here we are raising a web-server and microservice at the same time to receive requests for both web-based and RabbitMQ.


The scheme is as follows:


  1. The user connects to our web server.
  2. User is authorized by sending an authorize event with his userId inside
  3. We save the user's connection so that we can send notifications to it in the future.
  4. Event on RabbitMQ comes that it is necessary to send the notification to the user
  5. We check the validity of incoming data
  6. Get user connection
  7. We send a notification

We write microservice market


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

The scheme is as follows:


  1. We receive a user request to purchase an item
  2. We are looking for an item with the desired ID and make sure that it is not yet sold.
  3. Mark item as sold
  4. We send in the background a notice to the seller about the sale
  5. We respond to the client

Check


  1. We start 3 processes
  2. Send POST / market / buy / 1
  3. We get in response { ok: true }
  4. Seller receives notification

 $ 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