Terminus 集成为您提供就绪性/活跃性健康检查。健康检查对于复杂的后端设置至关重要。简而言之,Web 开发领域的健康检查通常由一个特殊地址组成,例如https://my-website.com/health/readiness
。
您的基础设施的服务或组件(例如 Kubernetes)会持续检查此地址。根据对此地址的 GET
请求返回的 HTTP 状态代码,服务将在收到不健康
响应时采取行动。
由于健康
或不健康
的定义因您提供的服务类型而异,因此 Terminus 集成为您提供了一组 健康指标。
例如,如果您的 Web 服务器使用 MongoDB 存储其数据,那么 MongoDB 是否仍在运行将是至关重要的信息。
在这种情况下,您可以使用 MongooseHealthIndicator
。如果配置正确(稍后会详细介绍),您的健康检查地址将返回
健康或不健康的 HTTP 状态代码,具体取决于 MongoDB 是否正在运行。
入门
要开始使用 @nestjs/terminus
,我们需要安装所需的依赖项。
$ npm install --save @nestjs/terminus
设置健康检查
健康检查代表健康指标的摘要。健康指标会检查服务是否处于健康或不健康状态。如果所有分配的健康指标都正常运行,则健康检查结果为阳性。由于许多应用程序都需要类似的健康指标,@nestjs/terminus
提供了一组预定义指标,例如:
HttpHealthIndicator
TypeOrmHealthIndicator
MongooseHealthIndicator
SequelizeHealthIndicator
MikroOrmHealthIndicator
PrismaHealthIndicator
MicroserviceHealthIndicator
GRPCHealthIndicator
MemoryHealthIndicator
DiskHealthIndicator
要开始进行第一次健康检查,让我们创建 HealthModule
并在其导入数组中导入 TerminusModule
。
要使用 Nest CLI 创建模块,只需执行 $ nest g module health
命令。
import { Module } from '@nestjs/common'
import { TerminusModule } from '@nestjs/terminus'
@Module({
imports: [TerminusModule]
})
export class HealthModule {}
我们的健康检查可以通过 控制器 执行,并且可以通过 Nest CLI 轻松设置。
$ nest g controller health
强烈建议在您的应用程序中启用关闭钩子。如果启用,Terminus 集成将使用此生命周期事件。阅读有关关闭钩子的更多信息此处。
HTTP 健康检查
一旦我们安装了 @nestjs/terminus
、导入了我们的 TerminusModule
并创建了一个新的控制器,我们就可以创建健康检查了。
HTTPHealthIndicator
需要 @nestjs/axios
包,因此请确保已安装它:
$ npm i --save @nestjs/axios axios
Now we can setup our HealthController
:
import { Controller, Get } from '@nestjs/common'
import { HealthCheck, HealthCheckService, HttpHealthIndicator } from '@nestjs/terminus'
@Controller('health')
export class HealthController {
constructor(
private health: HealthCheckService,
private http: HttpHealthIndicator,
) {}
@Get()
@HealthCheck()
check() {
return this.health.check([
() => this.http.pingCheck('nestjs-docs', 'https://docs.nestjs.com'),
])
}
}
import { Module } from '@nestjs/common'
import { TerminusModule } from '@nestjs/terminus'
import { HttpModule } from '@nestjs/axios'
import { HealthController } from './health.controller'
@Module({
imports: [TerminusModule, HttpModule],
controllers: [HealthController],
})
export class HealthModule {}
我们的健康检查现在将向 https://docs.nestjs.com
地址发送 GET 请求。如果我们从该地址获得健康响应,则 http://localhost:3000/health
处的路由将返回带有 200 状态代码的以下对象。
{
"status": "ok",
"info": {
"nestjs-docs": {
"status": "up"
}
},
"error": {},
"details": {
"nestjs-docs": {
"status": "up"
}
}
}
可以使用 HealthCheckResult
接口从 @nestjs/terminus
包访问此响应对象的接口。
status | 如果任何健康指标失败,状态将为 'error' 。如果 NestJS 应用程序正在关闭但仍在接受 HTTP 请求,则健康检查将具有 'shutting_down' 状态。 | 'error' | 'ok' | 'shutting_down' |
info | 包含状态为 'up' 的每个健康指标信息的对象,换句话说是健康 。 | object |
error | 包含状态为 'down' 的每个健康指标信息的对象,换句话说是不健康 。 | object |
details | 包含每个健康指标的所有信息的对象 | object |
检查特定的 HTTP 响应代码
在某些情况下,您可能需要检查特定条件并验证响应。例如,假设https://my-external-service.com
返回响应代码204
。使用HttpHealthIndicator.responseCheck
,您可以
专门检查该响应代码,并将所有其他代码确定为不健康。
如果返回除204
以外的任何其他响应代码,则以下示例将不健康。第三个参数
要求您提供一个函数(同步或异步),该函数返回一个布尔值,表示响应是被视为
健康(true
)还是不健康(false
)。
// Within the `HealthController`-class
@Get()
@HealthCheck()
check() {
return this.health.check([
() =>
this.http.responseCheck(
'my-external-service',
'https://my-external-service.com',
(res) => res.status === 204,
),
]);
}
TypeOrm 健康指示器
Terminus 提供了将数据库检查添加到健康检查的功能。为了开始使用此健康指示器,您 应该查看 数据库章节 并确保应用程序内的数据库连接已建立。
在后台,TypeOrmHealthIndicator
仅执行 SELECT 1
-SQL 命令,该命令通常用于验证数据库是否仍处于活动状态。如果您使用的是 Oracle 数据库,则它使用 SELECT 1 FROM DUAL
。
@Controller('health')
export class HealthController {
constructor(
private health: HealthCheckService,
private db: TypeOrmHealthIndicator,
) {}
@Get()
@HealthCheck()
check() {
return this.health.check([
() => this.db.pingCheck('database'),
])
}
}
如果您的数据库可以访问,那么在使用 GET
请求访问 http://localhost:3000/health
时您应该看到以下 JSON 结果:
{
"status": "ok",
"info": {
"database": {
"status": "up"
}
},
"error": {},
"details": {
"database": {
"status": "up"
}
}
}
如果您的应用使用 多个数据库,您需要将每个连接注入到您的 HealthController
。然后,您只需将连接引用传递给 TypeOrmHealthIndicator
。
@Controller('health')
export class HealthController {
constructor(
private health: HealthCheckService,
private db: TypeOrmHealthIndicator,
@InjectConnection('albumsConnection')
private albumsConnection: Connection,
@InjectConnection()
private defaultConnection: Connection,
) {}
@Get()
@HealthCheck()
check() {
return this.health.check([
() => this.db.pingCheck('albums-database', { connection: this.albumsConnection }),
() => this.db.pingCheck('database', { connection: this.defaultConnection }),
])
}
}
磁盘健康指示器
使用 DiskHealthIndicator
,我们可以检查正在使用的存储空间。首先,请确保将 DiskHealthIndicator
注入到您的 HealthController
中。以下示例检查路径 /
的存储使用情况(在 Windows 上,您可以使用 C:\\
)。如果超过总存储空间的 50% 以上,它将响应不健康的健康检查。
@Controller('health')
export class HealthController {
constructor(
private readonly health: HealthCheckService,
private readonly disk: DiskHealthIndicator,
) {}
@Get()
@HealthCheck()
check() {
return this.health.check([
() => this.disk.checkStorage('storage', { path: '/', thresholdPercent: 0.5 }),
])
}
}
使用DiskHealthIndicator.checkStorage
函数,您还可以检查固定的空间量。
如果路径/my-app/
超过 250GB,则以下示例将不健康。
// Within the `HealthController`-class
@Get()
@HealthCheck()
check() {
return this.health.check([
() => this.disk.checkStorage('storage', { path: '/', threshold: 250 * 1024 * 1024 * 1024, })
]);
}
内存健康指示器
要确保您的进程不超过一定的内存限制,可以使用 MemoryHealthIndicator
。
以下示例可用于检查进程的堆。
堆是动态分配内存所在的内存部分(即通过 malloc 分配的内存)。从堆分配的内存将保持分配状态,直到发生以下情况之一:
- 内存被释放
- 程序终止
@Controller('health')
export class HealthController {
constructor(
private health: HealthCheckService,
private memory: MemoryHealthIndicator,
) {}
@Get()
@HealthCheck()
check() {
return this.health.check([
() => this.memory.checkHeap('memory_heap', 150 * 1024 * 1024),
])
}
}
还可以使用 MemoryHealthIndicator.checkRSS
验证进程的内存 RSS。此示例
如果您的进程分配了超过 150MB 的内存,则将返回不健康的响应代码。
RSS 是驻留集大小,用于显示分配给该进程的内存量以及 RAM 中的内存量。
它不包括换出的内存。它包括来自共享库的内存,只要这些库中的页面确实在内存中。它包括所有堆栈和堆内存。
// Within the `HealthController`-class
@Get()
@HealthCheck()
check() {
return this.health.check([
() => this.memory.checkRSS('memory_rss', 150 * 1024 * 1024),
]);
}
自定义健康指标
在某些情况下,@nestjs/terminus
提供的预定义健康指标不能满足您的所有健康检查要求。在这种情况下,您可以根据需要设置自定义健康指标。
让我们开始创建一个代表我们自定义指标的服务。为了对指标的结构有一个基本的了解,我们将创建一个示例 DogHealthIndicator
。如果每个 Dog
对象都具有 'goodboy'' 类型,则此服务应具有
'up'' 状态。如果不满足该条件,则应该抛出错误。
import { Injectable } from '@nestjs/common'
import { HealthCheckError, HealthIndicator, HealthIndicatorResult } from '@nestjs/terminus'
export interface Dog {
name: string
type: string
}
@Injectable()
export class DogHealthIndicator extends HealthIndicator {
private dogs: Dog[] = [
{ name: 'Fido', type: 'goodboy' },
{ name: 'Rex', type: 'badboy' },
]
async isHealthy(key: string): Promise<HealthIndicatorResult> {
const badboys = this.dogs.filter(dog => dog.type === 'badboy')
const isHealthy = badboys.length === 0
const result = this.getStatus(key, isHealthy, { badboys: badboys.length })
if (isHealthy) {
return result
}
throw new HealthCheckError('Dogcheck failed', result)
}
}
接下来我们需要做的是将健康指标注册为提供者。
import { Module } from '@nestjs/common'
import { TerminusModule } from '@nestjs/terminus'
import { DogHealthIndicator } from './dog.health'
@Module({
controllers: [HealthController],
imports: [TerminusModule],
providers: [DogHealthIndicator]
})
export class HealthModule { }
在实际应用中,应在单独的模块(例如DogModule
)中提供DogHealthIndicator
,然后由HealthModule
导入。
最后一步是将现在可用的健康指标添加到所需的健康检查端点中。为此,我们返回HealthController
并将其添加到check
函数中。
import { HealthCheck, HealthCheckService } from '@nestjs/terminus'
import { Dependencies, Get, Injectable } from '@nestjs/common'
import { DogHealthIndicator } from './dog.health'
@Injectable()
export class HealthController {
constructor(
private health: HealthCheckService,
private dogHealthIndicator: DogHealthIndicator
) {}
@Get()
@HealthCheck()
healthCheck() {
return this.health.check([
() => this.dogHealthIndicator.isHealthy('dog'),
])
}
}
日志记录
Terminus 仅记录错误消息,例如当 Healthcheck 失败时。使用 TerminusModule.forRoot()
方法,您可以更好地控制如何记录错误
以及完全接管日志记录本身。
在本节中,我们将引导您了解如何创建自定义记录器 TerminusLogger
。此记录器扩展了内置记录器。
因此,您可以选择要覆盖记录器的哪一部分
如果您想了解有关 NestJS 中自定义记录器的更多信息,在此处阅读更多信息。
import { ConsoleLogger, Injectable, Scope } from '@nestjs/common'
@Injectable({ scope: Scope.TRANSIENT })
export class TerminusLogger extends ConsoleLogger {
error(message: any, stack?: string, context?: string): void
error(message: any, ...optionalParams: any[]): void
error(
message: unknown,
stack?: unknown,
context?: unknown,
...rest: unknown[]
): void {
// Overwrite here how error messages should be logged
}
}
一旦创建了自定义记录器,您需要做的就是将其传递到Terminus Module.forRoot()
中。
@Module({
imports: [
TerminusModule.forRoot({
logger: TerminusLogger,
}),
],
})
export class HealthModule {}
要完全抑制来自 Terminus 的任何日志消息(包括错误消息),请按此方式配置 Terminus。
@Module({
imports: [
TerminusModule.forRoot({
logger: false,
}),
],
})
export class HealthModule {}
Terminus 允许您配置如何在日志中显示健康检查错误。
错误日志样式 | 说明 | 示例 |
---|---|---|
json (默认) | 如果出现错误,则将健康检查结果摘要打印为 JSON 对象 | |
pretty | 如果出现错误,则在格式化的框内打印健康检查结果摘要,并突出显示成功/错误结果 |
您可以使用 errorLogStyle
配置选项更改日志样式,如以下代码片段所示。
@Module({
imports: [
TerminusModule.forRoot({
errorLogStyle: 'pretty',
}),
]
})
export class HealthModule {}
正常关闭超时
如果您的应用程序需要推迟其关闭过程,Terminus 可以为您处理。 此设置在使用 Kubernetes 等编排器时特别有用。 通过设置比就绪检查间隔稍长的延迟,您可以在关闭容器时实现零停机时间。
@Module({
imports: [
TerminusModule.forRoot({
gracefulShutdownTimeoutMs: 1000,
}),
]
})
export class HealthModule {}
更多示例
更多工作示例可在此处找到(https://github.com/nestjs/terminus/tree/master/sample)。