通过RabbitMQ学习在Node.js上的微服务之间进行通信

这是文章“ 我们正在通过RabbitMQ进行通信的Node.js上编写第一个微服务 ”的续篇,受到了Habr用户的好评。


在本文中,我将讨论如何在微服务之间正确通信,以使微服务保持隔离。


怎么不做


为什么需要在微服务之间进行通信? 使用一个数据库,从那里读取您想要的东西-做点事!


不,你不能那样做。 微服务的概念是它们彼此隔离,没有人了解任何人(实际上)。 将来很有可能,当系统开始增长时,您将需要扩展功能,并且需要在微服务之间进行通信:例如,用户购买了产品,因此您需要将有关销售的通知发送给卖方。


保温好处


可靠度


假设有一个单片应用程序,其中有多个控制器:


  1. 产品展示
  2. 打折
  3. 博客
  4. 用户数

好的一天,我们的数据库将要崩溃:现在我们找不到任何产品,没有折扣,没有博客文章,也没有用户。 该站点完全不可用,客户无法登录,业务损失了利润。


微服务架构会发生什么?


在另一个世界中,在用户微服务数据库崩溃的同一天,它变得不可访问:用户无法注销,注册和登录。 似乎一切都不好,企业也正在亏损,但是没有:潜在的买家可以查看可用的商品,阅读公司的博客并找到折扣。


由于每个微服务都有其自己的数据库,因此副作用要小得多。


这称为逐渐退化


抽象化


在大型应用程序中,很难集中精力完成一项任务,因为更改某些小型中间件可能会破坏某种控制器。 想要使用新客户端进行Redis-不,您不能,我们三年前编写的控制器使用版本0.1.0。 是否想最终利用Node.js 10的新功能? 还是12岁? 抱歉,但整体使用版本6。


如何沟通


由于我们已经开始讨论示例“用户购买了产品,因此我们向卖方发送了销售通知”,因此我们将其实施。


该方案如下:


  1. 用户向微服务市场发送请求以在链接/市场/购买/:id上购买商品
  2. 该标志写在销售产品的数据库中
  3. 从微服务市场,将请求发送到微服务通知,客户端通过WebSocket与之连接
  4. 微服务通知向卖家发送有关商品销售的消息

安装MicroMQ


$ npm i micromq@1 -S 

编写网关


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

我们的网关仅包含一个端点,但这足以作为示例和培训。


编写微服务通知


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

在这里,我们同时提高了Web套接字服务器和微服务,以接收对Web套接字和RabbitMQ的请求。


该方案如下:


  1. 用户连接到我们的Web套接字服务器
  2. 通过发送内部包含其userId的authorize事件来授权用户
  3. 我们保持用户的联系,以便您以后可以向他发送通知
  4. 事件到达RabbitMQ,您需要向用户发送通知
  5. 检查传入数据的有效性
  6. 获取用户连接
  7. 发送通知

编写微服务市场


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

该方案如下:


  1. 我们收到用户购买物品的请求
  2. 我们正在寻找具有正确ID的商品,并确保尚未出售
  3. 将商品标记为已出售
  4. 我们会在后台向卖家发送有关销售的通知
  5. 我们回应客户

检查一下


  1. 我们开始3个过程
  2. 我们将发送POST /市场/购买/ 1
  3. 我们得到了回应{ ok: true }
  4. 卖家收到通知

 $ 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/zh-CN447250/


All Articles