- 后端
- 微服务
- 云原生
【免费下载链接】midway
🍔 A Node.js Serverless Framework for front-end/full-stack developers. Build the application for next decade. Works on AWS, Alibaba Cloud, Tencent Cloud and traditional VM/Container. Super easy integrate with React and Vue. 🌈
导读
本文基于 Midway 官方子包@midwayjs/consul(本仓库位于 packages/consul),系统讲解如何在 Midway 应用中集成 Consul,实现服务注册(register)、应用下线反注册(deregister)、基于健康检查实例的服务发现与负载均衡,以及直接注入原生 Consul 客户端进行底层操作。读完本文,你将掌握从安装依赖、编写配置、注册服务,到通过consul:balancerService调用远端服务实例的完整链路,并了解服务发现模块(ConsulServiceDiscovery)背后的源码实现与健康检查机制,可直接在基于 Koa / Express / Egg 的 Midway 微服务项目中落地使用。
一、Consul 组件能做什么
@midwayjs/consul是 Midway 生态中面向微服务注册与发现的标准组件,能力清单如下(与 packages/consul/usage.md 中 Support 列表一致):
- register(可选):应用启动时向 Consul Server 注册当前服务实例;
- deregister on the shutdown(可选):应用正常关闭时自动反注册,避免留下“僵尸实例”;
- service balancer(默认 random):组件内置负载均衡器,在多个健康实例中挑选一个进行调用,默认采用随机策略;
- expose the origin consul object:暴露原始
consul客户端对象,方便直接使用官方库的底层 API(如 KV 存储、健康检查查询等)。
组件基于consulnpm 包(dependencies中锁定consul@2.0.1)封装,运行要求 Node.js >= 20,详见 packages/consul/package.json。
二、安装与引入组件
2.1 安装依赖
在 Midway 项目中安装运行时依赖与 TypeScript 类型声明:
npm i @midwayjs/consul -S npm i @types/consul -D-S:安装到dependencies,运行时需要;-D:安装@types/consul到devDependencies,仅 TypeScript 编译期使用。
2.2 引入组件
在入口configuration.ts中,通过imports引入consul模块,并通过importConfigs声明配置目录:
import * as consul from '@midwayjs/consul' @Configuration({ imports: [ consul ], importConfigs: [join(__dirname, 'config')] }) export class ContainerConfiguration {}组件内部在ConsulConfiguration(见 packages/consul/src/configuration.ts)中完成了生命周期管理:
onReady:应用就绪时通过容器获取ConsulServiceFactory,提前完成客户端初始化;onStop:应用停止时调用factory.stop(),触发destroyClient销毁连接(packages/consul/src/manager.ts 中会打印[midway:consul] destroy %s日志)。
因此你只需引入组件并写好配置,连接的生命周期由框架自动托管。
三、配置 Consul Server 与服务定义
在配置目录(即importConfigs指向的config文件夹)下的config.default.ts或config.default.js中写入:
consul: { provider: { // 注册本服务 register: true, // 应用正常下线反注册 deregister: true, // consul server 主机 host: '192.168.0.10', // consul server 端口 port: 8500, // 调用服务的策略(默认选取 random 具有随机性) strategy: 'random', }, service: { address: '127.0.0.1', port: 7001, tags: ['tag1', 'tag2'], // others consul service definition } }各字段含义与取值说明:
| 配置段 | 字段 | 说明 | 默认值/备注 |
|---|---|---|---|
provider | register | 是否向 Consul 注册当前服务 | 布尔值,可选;为true时启动即注册 |
provider | deregister | 应用正常关闭时是否反注册 | 布尔值,可选;为true时onStop阶段调用agent.service.deregister |
provider | host | Consul Server 地址 | 例如192.168.0.10 |
provider | port | Consul Server 端口 | 默认8500(Consul HTTP API 标准端口) |
provider | strategy | 实例选取策略 | 默认random,具有随机性 |
service | address | 本服务对外暴露的 IP/主机 | 例如127.0.0.1 |
service | port | 本服务对外暴露的端口 | 例如7001 |
service | tags | 注册时附加的标签数组 | 例如['tag1', 'tag2'],可用于按标签筛选实例 |
service | 其余字段 | 其他 Consul 服务注册定义 | 直接透传给官方agent.service.register |
从源码看,客户端工厂ConsulServiceFactory.createClient(packages/consul/src/manager.ts)会把consul配置中的host、port等直接作为参数构造原生new Consul(config)客户端,并在初始化时输出日志[midway:consul] init %s at %s:%s,方便排查连接目标。
3.1 关于服务发现(serviceDiscovery)的进阶配置
除了上述provider/service两段,组件默认配置中还预置了服务发现相关参数(见 packages/consul/src/configuration.ts):
consul: { serviceDiscovery: { loadBalancer: 'roundRobin', healthCheckType: 'self', }, }loadBalancer:服务发现客户端的负载均衡器类型,组件默认值为roundRobin;healthCheckType:健康检查方式,默认self(自管理健康检查)。
ConsulServiceDiscovery(packages/consul/src/extension/serviceDiscovery.ts)在初始化时通过MidwayConfigService.getConfiguration('consul.serviceDiscovery', {})读取该段配置,并据此创建对应的服务发现客户端。
四、查询服务实例并调用(负载均衡)
当需要调用其他注册到 Consul 的服务时,通过consul:balancerService获取负载均衡器。支持两种获取方式:
// 1. 注入的方式 @Inject('consul:balancerService') balancerService: IConsulBalancer; // 2. 编码的方式 const balancerService = await app.getApplicationContext().getAsync<IConsulBalancer>('consul:balancerService');然后通过getBalancer().select(name)选取一个实例:
// 1. 查询通过健康检查的服务 const service = await balancerService.getBalancer().select('the-service-name'); // 2. 可能取到不健康的服务 const service = await balancerService.getBalancer().select('the-service-name', false);两点必须注意:
- 返回的是 Consul 原生数据:
select返回的service是 Consul API 返回的原始健康检查条目结构(含Node、Service、Checks等字段,类型定义见 packages/consul/src/interface.ts 中的ConsulHealthItem),组件并不知道应用层使用 Consul 的哪些元数据信息,因此不做过多的结构转换; - 无实例时会抛 Error:
select在“没有任何可用服务实例”的场景下会抛出异常,业务代码需要自行捕获或降级处理。
select的第二个参数控制是否只选取健康实例:默认(true/缺省)仅返回通过健康检查的实例;传入false时则可能取到不健康的实例。
4.1 与 Service Discovery 的关系
consul:balancerService的负载均衡能力建立在服务发现之上。ConsulServiceDiscovery对外提供以下核心方法(见 packages/consul/src/extension/serviceDiscovery.ts):
createClient():创建服务发现客户端,内部实现register/deregister/online/offline/beforeStop等方法;getInstances(options):获取某个服务当前的健康实例列表(内部强制passing: true,即只返回健康检查通过的实例);getInstance(options):从实例列表中选取单个实例。
其数据链路可以总结为:
ConsulDataListener首次调用client.health.service(options)拉取全量实例作为初始数据(initData);- 随后通过
client.watch({ method: client.health.service, options })建立 Consul watch 长轮询监听,实例列表变化时自动setData更新(见onData); - 多个查询请求按
hashServiceOptions(基于排序 key 的 DJB2 变体哈希,见 packages/consul/src/utils.ts)做缓存,同一服务复用同一个ConsulDataListener,避免重复建立 watch; - 应用停止时
beforeStop统一销毁所有 listener 的 watcher。
从源码结构看,ConsulServiceDiscoverClient.register的流程还包括:注册成功后调用online()把实例状态置为UP(内部走agent.check.pass(checkId),checkId 形如service:${instance.id}),offline()则对应agent.check.fail。测试用例 packages/consul/test/serviceDiscovery.test.ts 中完整演示了注册(携带check: { name, timeout, ttl }的 TTL 健康检查)、getInstances查询、getInstance选取,以及offline()后健康实例列表归零的完整流程。
五、注入原生 Consul 对象
除了组件封装的高层能力,还可以直接注入官方consul客户端,使用其完整 API:
import * as Consul from 'consul'; // 使用 consul 官方包装的 API 接口 @Inject('consul:consul') consul: Consul.Consul;注入后即可直接调用官方库方法,例如:
// KV 读写 await this.consul.kv.set('my/key', 'my-value'); const res = await this.consul.kv.get('my/key'); // 查询健康服务 const services = await this.consul.health.service({ service: 'the-service-name' });底层实现上,ConsulService(packages/consul/src/manager.ts)是一个@Singleton(),初始化时从ConsulServiceFactory中取出默认客户端(getDefaultClientName()未定义时取'default'),并通过delegateTargetAllPrototypeMethod(ConsulService, Consul)把原生 Consul 类的原型方法全部委托到该实例上,因此注入后具备与原生客户端完全一致的方法集。
5.1 多客户端与自定义客户端
ConsulServiceFactory继承自 Midway 的ServiceFactory,支持按consul.client.xxx的形式配置多个客户端(测试用例 packages/consul/test/index.test.ts 中展示了consul.client.host/port的配置写法)。此外createClient支持customClientClass配置项——若传入自定义类,则使用new customClientClass(otherConfig)构造客户端(packages/consul/test/customClient.test.ts 有对应用例),适合替换或包装客户端实现的场景。
六、健康检查机制(源码级解读)
当service定义中携带check字段且autoHealthCheck开启时,ConsulServiceDiscoverClient.register会根据检查类型自动启动对应的健康检查器(实现位于 packages/consul/src/extension/helper.ts):
| 检查类型 | 触发条件 | 实现类 | 行为 |
|---|---|---|---|
| TTL 心跳 | check.ttl存在 | TTLHeartbeat | 按calculateTTL(check.ttl)计算间隔(最小 1000ms,支持"30s"、"1m"等 ms 库格式),周期性调用agent.check.pass(checkId)上报心跳,失败时输出错误日志 |
| HTTP 探活 | check.http存在 | HTTPHealthCheck | 解析 URL 的端口与路径,若当前应用是 Egg/Koa/Express 应用则把该路径注册进框架路由返回success;否则自起一个 HTTP 服务在对应端口路径返回ok |
| TCP 探活 | check.tcp存在 | TCPHealthCheck | 自起一个 TCP 服务监听对应端口/主机,连接成功即关闭,用于验证端口可达 |
应用关闭时,beforeStop会依次停止 TTL 心跳与 HTTP/TCP 健康检查服务,并完成deregister()反注册,确保资源释放干净。
七、完整的最小可运行示例
综合以上内容,一个完整的最小接入方案如下:
1. 安装:
npm i @midwayjs/consul -S npm i @types/consul -D2. 入口 configuration.ts:
import { Configuration } from '@midwayjs/core'; import { join } from 'path'; import * as consul from '@midwayjs/consul'; @Configuration({ imports: [consul], importConfigs: [join(__dirname, 'config')], }) export class ContainerConfiguration {}3. 配置 config/config.default.ts:
export default { consul: { provider: { register: true, deregister: true, host: '192.168.0.10', port: 8500, strategy: 'random', }, service: { address: '127.0.0.1', port: 7001, tags: ['tag1', 'tag2'], check: { name: 'TTL Health Check', timeout: '30s', ttl: '10s', }, }, }, };4. 调用远端服务:
import { Inject, Provide } from '@midwayjs/core'; import { IConsulBalancer } from '@midwayjs/consul'; @Provide() export class RemoteCallService { @Inject('consul:balancerService') balancerService: IConsulBalancer; async call() { // 只选健康实例 const healthy = await this.balancerService.getBalancer().select('user-service'); // 或允许不健康实例 const any = await this.balancerService.getBalancer().select('user-service', false); return { healthy, any }; } }八、常见问题与注意事项
- select 抛错:没有可用的服务实例时
select会抛出Error,请务必 try/catch 或结合降级逻辑(如直接返回默认地址)使用; - 注册与反注册的开关:
register/deregister均为可选开关,生产环境建议都开启,避免服务下线后实例仍在 Consul 中残留; - 策略选择:
provider.strategy默认random,若需均匀分发可关注serviceDiscovery.loadBalancer的roundRobin配置(组件默认即roundRobin); - 返回数据为原生结构:
select/getInstances返回的是 Consul 健康检查原始数据结构(Node/Service/Checks),字段名遵循 Consul API 的大写风格,需按 ConsulHealthItem 类型定义取用; - 多环境配置:Midway 的
importConfigs机制支持config.default/config.prod/config.test等按环境覆盖 Consul 地址与开关,便于本地与线上隔离。
九、更多源码入口
- 组件生命周期与默认配置:packages/consul/src/configuration.ts
- 客户端工厂、原生客户端注入与委托:packages/consul/src/manager.ts
- 服务发现与数据监听(watch、缓存、注册/反注册):packages/consul/src/extension/serviceDiscovery.ts
- TTL 心跳 / HTTP / TCP 健康检查实现:packages/consul/src/extension/helper.ts
- 类型定义(
ConsulHealthItem、GetHealthServiceOptions、ConsulServiceDiscoveryOptions):packages/consul/src/interface.ts - 官方用法文档:packages/consul/usage.md
- 功能测试用例:packages/consul/test/index.test.ts 与 packages/consul/test/serviceDiscovery.test.ts
- 后端
- 微服务
- 云原生
【免费下载链接】midway
🍔 A Node.js Serverless Framework for front-end/full-stack developers. Build the application for next decade. Works on AWS, Alibaba Cloud, Tencent Cloud and traditional VM/Container. Super easy integrate with React and Vue. 🌈
相关推荐
gh_mirrors/docume/documentation微前端架构:大型项目的模块化拆分方案
gh_mirrors/docume/documentation微前端架构:大型项目的模块化拆分方案 gh_mirrors/docume/documentatio
微服务架构中Consul健康检查的终极指南:确保服务高可用的关键步骤
微服务架构中Consul健康检查的终极指南:确保服务高可用的关键步骤 在微服务架构中,服务注册与发现是确保系统弹性和可靠性的核心组件。Consul作为一款功能全
文档教程知识库技术博客后端抖音无水印批量下载:3步搞定,备份创作者全部作品
抖音无水印批量下载:3步搞定,备份创作者全部作品 douyin downloader 是一个实用的抖音批量下载工具,主打无水印下载。它直接解析作品的原始文件地址
网页爬虫CLI
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考