• 0

  • 496

Node.js微服务框架(Moleculer)

智能的司机

我是老司机

1星期前

如果想在 Node.js 项目中使用微服务框架的话,推荐 Moleculer,它有丰富的功能、完善的文档、高效的性能和灵活的配置,我们先看一张架构图:

架构

上图描述了如下的应用场景:

  1. 用户请求通过 HTTP 协议到达 API 网关层
  2. Gateway 服务解析请求并将数据和指令交给 Broker
  3. Broker 通过 Transporter 调用微服务内部函数,返回处理后的数据

从这张图可以看到 Moleculer 接管了内部的服务注册与发现、服务之间的消息通信以及负载均衡等底层复杂逻辑,用户只需要写核心业务逻辑即可,大大提升了开发效率。

核心概念

服务 Service

服务就是一个独立的业务单元,本质上就是一个 JS 对象,例如下面的代码实现了完整的加减乘除功能,我们可以给它起名为 math 服务:

module.exports = {
  name: 'math',
  actions: {
    add(ctx) {
      const { a, b } = ctx.params
      return a + b
    },
    subtract(ctx) {
      const { a, b } = ctx.params
      return a - b
    }
    // 省略代码
  },
}
复制代码

节点 Node

节点就是本地或网络上的一个 Node 进程,每个节点内部可以承载多个服务。例如定义了数学计算 math 和统计分析 statistic 两个服务,放在了 services 目录下,目录结构为:

services
├── math.service.js
└── statistic.service.js
复制代码

现将其在同一个 Node 进程中启动。

const { ServiceBroker } = require('moleculer')
const broker = new ServiceBroker()
broker.loadServices('./services')
broker.start().catch((e) => console.error(e))
复制代码

那么这个启动的 Node 进程就是一个 Moleculer 节点。

本地服务 Local services

在同一个节点上的服务彼此互为本地服务,它们共享硬件资源,通过局部总线进行通信,无任何网络延迟。比如说上面的 mathstatistic 服务之间互为本地服务,可以在 statistic 里面直接调用 math 中的方法,这个时候并没有走传输介质,而是进程内部调用,效率很高。

module.exports = {
  name: 'statistic',
  actions: {
    calculate(ctx) {
      return ctx.call(`math.add`, { a: 3, b: 4 })
    },
  },
}
复制代码

远程服务 Remote Services

不同节点上的服务彼此互为远程服务,它们之间通过某种传输介质(例如消息中间件)进行通信。例如我们又起了一个 Node 进程,里面加载了 useraccount 服务:

services
├── account.service.js
└── user.service.js
复制代码

那么 usermath 之间就互为远程服务,因为它们不在一个 Node 进程下面,例如在 user.get 中调用另外一个进程的 math.multiply 方法,就必须通过传输介质进行通信。

module.exports = {
  name: 'user',
  actions: {
    async get(ctx) {
      return ctx.call('math.multiply', { a: 3, b: 10 })
    },
  },
}
复制代码

服务中介 Service Broker

服务中介是 Moleculer 框架的核心,负责服务之间的通信(包括本地和远程服务),每个节点都有一个服务中介。

const { ServiceBroker } = require('moleculer')
const broker = new ServiceBroker()
复制代码

broker 实例有很多方法,例如:

  • 通过 loadService 方法加载服务
  • 通过 emit 方法触发事件
  • 通过 call 方法发送请求

传输介质 Transporter

传输介质负责节点之间的通信,是服务之间传输消息的通信总线,可以触发事件、发送请求、接收响应。

传输介质

也就是说如果 Node1 中的 user 服务想发消息给 Node2 中的 math 服务,需要通过传输介质进行通信,Moleculer 中提供了多种传输介质,例如:

  • nats(yarn add nats
  • redis(yarn add ioredis
  • kafka(yarn add kafka-node
  • mqtt(yarn add mqtt
  • amqp(yarn add amqplib
  • 等等,还可以自定义传输介质
const { ServiceBroker } = require('moleculer')
const broker = new ServiceBroker({
  transporter: 'nats://localhost:4222', // 使用 nats 传输介质
  // transporter: 'redis://localhost:6379', // 使用 redis 传输介质
  // transporter: 'kafka://localhost:2181', // 使用 kafka 传输介质
  // transporter: 'mqtt://localhost:1883', // 使用 mqtt 传输介质
  // transporter: "amqp://localhost:5672", // 使用 amqp 传输介质
})
复制代码

网关 Gateway

网关将内部服务暴露给终端用户,它就是一个常规的 Moleculer 服务,内部集成了 HTTP 或 WebSocket 功能,将用户请求转发到内部服务。官方提供了一个 RESTful 的 API 网关:

yarn add moleculer-web # 或者 npm i moleculer-web 
复制代码

使用方法:

const { ServiceBroker } = require('moleculer')
const ApiService = require('moleculer-web')
const broker = new ServiceBroker({ transporter: 'nats://localhost:4222' })
broker.createService(ApiService)
broker.start()
复制代码

这个时候,默认会在 localhost:3000 端口开启一个 RESTful 的 HTTP 服务:

curl 'localhost:3000/math/subtract?a=5&b=3'
复制代码

如果 math 服务启动,那么请求就会被 Moleculer 转发给 math.subtract 方法进行处理并返回结果 2

命令行工具

Moleculer 最让开发者喜欢的一点就是它提供了非常强大的命令行工具 moleculer-repl,采用约定大于配置的规范自动加载 services 目录下 xxx.service.js 文件,在开发环境支持热加载,也就是说自动监听代码变化,保存即更新服务,无需重启。在 package.json 中配置如下:

"scripts": {
  "dev": "moleculer-runner --repl --hot services",
  "start": "moleculer-runner"
},
复制代码

在 moleculer-repl 提供的命令行里面可以看到所有的方法和事件:

repl

还可以直接调试各种方法,例如:

call "math.add" --a 5 --b 6 # 命令行传参
call "math.add" --load my-params.json # 从 文件中加载参数
call "math.add" --save my-response.json # 把返回结果保存到文件
复制代码

而且 moleculer-runner 可以直接上生产环境,自带日志管理、错误调试、缓存、参数校验等功能。

数据库连接

Moleculer 封装了 moleculer-db 库,把 CRUD 的逻辑打包进去,并且对接了 mongo、mongoose 和 sequelize ORM 框架,增删改查不需要写一行代码,只要引入相应的库和适配器即可:

const DbService = require('moleculer-db')
const mongoose = require('mongoose')
const MongooseAdapter = require('moleculer-db-adapter-mongoose')
复制代码

然后在创建服务的时候,添加下面的参数:

broker.createService({
  name: 'order',
  mixins: [DbService],
  adapter: new MongooseAdapter('mongodb://localhost:27017/test'),
  model: mongoose.model(
    'order',
    mongoose.Schema({
      orderNo: { type: String },
      price: { type: Number, default: 0 },
    })
  ),
})
复制代码

这个时候会自动生成 countcreatefindgetinsertlistremoveupdate 八个方法,开箱即用。

中间件

Moleculer 同样支持中间件,框架有内置的中间件,用户也可以定义自己的中间件,还是 express 中熟悉的洋葱模型:

中间件

在 Moleculer 中,中间件本质上就是一个包含高阶函数的对象,可以应用在方法、事件等各种场景,例如下面的错误捕获中间件:

module.exports = {
  name: 'error',
  localAction(next, action) {
    return async function (ctx) {
      try {
        return await next(ctx)
      } catch (e) {
        console.log('出错的Action', action.name)
        throw e
      }
    }
  }
}
复制代码

上面只是初步介绍了 Moleculer 的核心知识点,其实还有非常多强大的功能,感兴趣的可以参阅官方文档

本文示例代码地址:git clone git@github.com:keliq/moleculer-demo.git

免责声明:文章版权归原作者所有,其内容与观点不代表Unitimes立场,亦不构成任何投资意见或建议。

496

相关文章推荐

未登录头像

暂无评论