3-4 使用Terminus创建健康检查Endpoint并集成Consul
问题背景
微服务端改为纯 gRPC 模式后,不再暴露 RESTful 端口。但 Consul 的 HTTP 健康检查只能请求 HTTP 接口。解决方案:
| 方案 | 优点 | 缺点 |
|---|---|---|
| 独立健康检查服务 | 统一管理 | 新增/删除服务需要重启 |
| Gateway 代理健康检查 | 利用现有服务 | 增加网关职责 |
| Terminus + Gateway(推荐) | 标准化、可扩展 | 需要引入新依赖 |
推荐方案:在 Gateway 中使用 @nestjs/terminus 提供统一的健康检查 RESTful 端点,通过 gRPC 调用各微服务的健康检查接口。
@nestjs/terminus 简介
Terminus 是 NestJS 官方的健康检查库,提供了多种内置 Health Indicator:
| Indicator | 用途 | 检查方式 |
|---|---|---|
| GRPCHealthIndicator | gRPC 服务健康检查 | 调用 gRPC Health.Check 方法 |
| MicroserviceHealthIndicator | 微服务检查 | TCP 连接检测 |
| TypeOrmHealthIndicator | 数据库检查 | 执行 SELECT 1 |
| DNSHealthIndicator | DNS 检查 | 解析域名 |
| HttpHealthIndicator | HTTP 检查 | 发送 HTTP 请求 |
安装
cd packages/gateway
pnpm add @nestjs/terminus
bash
GRPCHealthIndicator 源码分析
通过阅读 Terminus 源码了解其工作原理:
健康检查结果结构
interface HealthIndicatorResult {
key: string; // 唯一标识(如 "user-grpc")
status: 'up' | 'down'; // 健康状态
// 可扩展的其他状态字段
}
typescript
核心检查逻辑
GRPCHealthIndicator.check()
└── 使用 gRPC 配置连接目标服务
└── 调用 Health.Check 方法
└── 比较 response.status === ServingStatus.SERVING (1)
├── 相等 → isHealthy = true → status: 'up'
└── 不等 → isHealthy = false → status: 'down'
text
关键参数
| 参数 | 说明 |
|---|---|
key | 健康检查的唯一标识 |
service | proto 中定义的 service 名称(默认为 "health") |
options | gRPC 连接配置(url、package、protoPath 等) |
timeout | 检查超时时间 |
Gateway 中的 Consul Controller 实现
创建 Controller
nest g co consul --no-spec
bash
实现 Health Check 端点
// src/consul/consul.controller.ts
import { Controller, Get } from '@nestjs/common';
import {
HealthCheck,
HealthCheckService,
GRPCHealthIndicator,
} from '@nestjs/terminus';
import { loadProto } from '@remote/proto-pkg';
@Controller('health')
export class ConsulController {
constructor(
private health: HealthCheckService,
private grpc: GRPCHealthIndicator,
) {}
@Get()
@HealthCheck()
check() {
return this.health.check([
() =>
this.grpc.checkHealth('user-grpc', {
url: 'localhost:40001',
package: 'user',
protoPath: loadProto('user'),
}),
]);
}
}
typescript
注册 Module
// src/consul/consul.module.ts
import { Module } from '@nestjs/common';
import { TerminusModule } from '@nestjs/terminus';
import { ConsulController } from './consul.controller';
@Module({
imports: [TerminusModule],
controllers: [ConsulController],
})
export class ConsulModule {}
typescript
常见问题与解决
问题一:proto definition not found
原因:GRPCHealthIndicator 内部 bug,service 参数默认值为 "health"。
解决:将 proto 中的 service 名称从 HealthService 改为 Health(匹配默认值),或在调用时显式指定:
this.grpc.checkHealth('user-grpc', {
url: 'localhost:40001',
package: 'user',
protoPath: loadProto('user'),
serviceName: 'health', // 显式指定 proto 中的 service 名称
});
typescript
问题二:proto 中的 service 名称不匹配
将 health.proto 中的 service 名称统一改为 Health:
service Health {
rpc Check(HealthCheckRequest) returns (HealthCheckResponse);
}
protobuf
这样 Terminus 默认查找的 "health" 就能匹配上。
Consul 注册配置更新
微服务注册时,将健康检查地址指向 Gateway 的健康端点:
// user 微服务中 Consul 注册配置
await consul.agent.service.register({
name: 'user-service',
address: '192.168.1.100',
port: 40001,
check: {
http: 'http://gateway-host:3030/health', // 指向 Gateway 健康端点
interval: '10s',
},
});
typescript
完整链路
Consul Server 定期检查
└── HTTP GET http://gateway:3030/health
└── Gateway ConsulController
└── Terminus HealthCheckService
└── GRPCHealthIndicator
└── gRPC 调用 user-service:40001/Health/Check
├── status: 'up' → Consul 标记服务健康
└── status: 'down' → Consul 标记服务异常
text
参考资源
- NestJS Terminus - 健康检查官方文档
- Terminus GitHub - 源码和示例
- gRPC Health Checking - gRPC 健康检查协议
↑