为什么需要拆分健康检查模块
在之前的架构中,健康检查的 Controller(ConsulController)被直接放在了 Gateway 项目中。这会导致一个实际问题:当开发过程中频繁重启 Gateway 服务时,Consul Server 可能无法收到健康检查的响应(HTTP 200),进而将服务标记为不健康并触发下线。这会严重干扰开发和调试流程。
解决方案是将健康检查逻辑抽离为一个独立的微服务项目,这样即使 Gateway 在重启,健康检查服务依然可以稳定运行,持续向 Consul Server 报告服务状态。
创建独立的 Health 项目
1. 在 Monorepo 中创建新项目
项目采用 pnpm workspace 的 monorepo 架构,所有微服务共享 proto-pkg 等公共模块:
# 在项目根目录执行
cd /path/to/project-root
nest new health
# 选择 pnpm 作为包管理工具
bash
创建完成后,将新项目加入 pnpm workspace 配置:
// pnpm-workspace.yaml
packages:
- "apps/*"
- "libs/*"
- "health" # 新增
json
2. 从 Gateway 迁移 Consul 相关代码
需要迁移的核心文件包括:
health/src/
├── consul/
│ ├── consul.module.ts # Consul 模块(保留 forRoot 中的 Controller 部分)
│ └── consul.controller.ts # 健康检查 Controller
├── config/
│ ├── config.module.ts # 配置模块
│ └── config.service.ts # 配置服务
├── app.module.ts
└── main.ts
text
关键清理步骤:
- 删除 ConsulService(Gateway 专用,Health 项目不需要)
- 删除不必要的 interface 文件
- 删除 constants 常量文件
- 只保留 Terminus 模块和 ConsulController 相关代码
3. 安装必要依赖
cd health
pnpm add @nestjs/microservices @nestjs/terminus
# 安装 SWC 编译器加速开发
pnpm add -D @swc/cli @swc/core
bash
4. 配置 SWC 编译加速
修改 nest-cli.json 启用 SWC 编译:
{
"compilerOptions": {
"builder": "swc"
}
}
json
SWC 是基于 Rust 编写的 JavaScript/TypeScript 编译器,编译速度比默认的 tsc 快 10-20 倍,在开发过程中能显著提升热重载速度。
端口与路由配置
避免端口冲突
Health 服务的端口需要与 Gateway 的端口错开:
// health/src/main.ts
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { ConfigService } from './config/config.service';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
const configService = app.get(ConfigService);
app.setGlobalPrefix('api');
app.enableVersioning();
await app.listen(configService.get('PORT') || 3030);
}
bootstrap();
typescript
端口分配方案:
| 服务 | 端口 | 说明 |
|---|---|---|
| Health(健康检查) | 3030 | 响应 Consul 的健康检查请求 |
| Gateway(网关) | 3040 | 对外提供 API 接口(修改为 3040 避免冲突) |
| User Service 1 | 40001 | 用户微服务实例 1 |
| User Service 2 | 40002 | 用户微服务实例 2 |
| User Service (DC2) | 40003 | 数据中心 2 的用户微服务 |
配置 .env 文件
从 Gateway 复制 .env 配置文件到 Health 项目,保持 Consul 连接配置一致:
# 复制公共配置
cp -r apps/gateway/src/config health/src/config
bash
注册 ConsulModule
在 app.module.ts 中正确注册 ConsulModule:
// health/src/app.module.ts
import { Module } from '@nestjs/common';
import { ConfigModule } from './config/config.module';
import { ConsulModule } from './consul/consul.module';
@Module({
imports: [
ConfigModule,
ConsulModule.forRoot(), // 调用 forRoot 初始化 Consul 连接
],
})
export class AppModule implements OnModuleInit {
onModuleInit() {
console.log('Health check service initialized');
}
}
typescript
配置 package.json 私有前缀
为了在 pnpm workspace 中正确识别项目:
// health/package.json
{
"name": "@app/health",
"version": "0.0.1",
"private": true,
"scripts": {
"dev": "nest start --watch",
"build": "nest build"
}
}
json
同时在根目录的 package.json 中添加启动脚本:
{
"scripts": {
"dev:health": "pnpm --filter @app/health dev"
}
}
json
启动与验证
启动顺序
# 1. 启动 Consul Server(Docker)
docker compose -f docker-compose.consul.yml up -d
# 2. 启动用户微服务
pnpm dev:user
pnpm dev:user1
# 3. 启动健康检查服务
pnpm dev:health
bash
验证步骤
- 打开 Consul UI(http://localhost:8500),确认服务已注册
- 检查 Consul 日志是否显示健康检查请求
- 通过浏览器访问健康检查接口:
curl http://localhost:3030/api/v1/health
bash
正常情况下应该返回:
{
"status": "ok",
"info": {
"user-grpc": { "status": "up" }
}
}
json
后续优化方向
拆分完成后,Health 服务在以下方面还可以继续优化:
- 共享模块抽离:将 ConsulModule、ConfigModule 抽离到独立的 npm 包中,避免各微服务重复维护
- 健康检查粒度:除了 gRPC 服务状态,还可以加入数据库连接、Redis 连接等维度的健康检查
- 监控告警集成:接入 Prometheus + Grafana,对健康状态变化进行实时监控和告警
↑