健康检查

健康检查(Health checks)

健康检查用于确保你的应用在生产环境运行时处于健康状态。这可能包括监控服务器上可用的磁盘空间应用消耗的内存,或测试数据库连接

AdonisJS 提供了一些内置的健康检查,以及创建和注册自定义健康检查的能力。

配置健康检查

你可以通过执行以下命令在应用中配置健康检查。该命令将创建一个 start/health.ts 文件,并为内存使用已用磁盘空间配置健康检查。你可以随意修改此文件,移除/添加额外的健康检查。

在使用以下命令之前,请确保你已安装 @adonisjs/[email protected]

node ace configure health_checks
start/health.ts
import { HealthChecks, DiskSpaceCheck, MemoryHeapCheck } from '@adonisjs/core/health'
export const healthChecks = new HealthChecks().register([
new DiskSpaceCheck(),
new MemoryHeapCheck(),
])

暴露一个端点

执行健康检查的常见做法是暴露一个 HTTP 端点,外部监控服务可以 ping 该端点以收集健康检查结果。

因此,让我们在 start/routes.ts 文件中定义一个路由,并将 HealthChecksController 绑定到它。health_checks_controller.ts 文件在初始设置期间创建,位于 app/controllers 目录中。

start/routes.ts
import router from '@adonisjs/core/services/router'
const HealthChecksController = () => import('#controllers/health_checks_controller')
router.get('/health', [HealthChecksController])

报告示例

healthChecks.run 方法将执行所有检查,并返回一份详细的以 JSON 对象形式呈现的报告。该报告具有以下属性:

{
"isHealthy": true,
"status": "warning",
"finishedAt": "2024-06-20T07:09:35.275Z",
"debugInfo": {
"pid": 16250,
"ppid": 16051,
"platform": "darwin",
"uptime": 16.271809083,
"version": "v21.7.3"
},
"checks": [
{
"name": "Disk space check",
"isCached": false,
"message": "Disk usage is 76%, which is above the threshold of 75%",
"status": "warning",
"finishedAt": "2024-06-20T07:09:35.275Z",
"meta": {
"sizeInPercentage": {
"used": 76,
"failureThreshold": 80,
"warningThreshold": 75
}
}
},
{
"name": "Memory heap check",
"isCached": false,
"message": "Heap usage is under defined thresholds",
"status": "ok",
"finishedAt": "2024-06-20T07:09:35.265Z",
"meta": {
"memoryInBytes": {
"used": 41821592,
"failureThreshold": 314572800,
"warningThreshold": 262144000
}
}
}
]
}

isHealthy

一个布尔值,用于判断是否所有检查都已通过。如果一个或多个检查失败,该值将被设为 false

status

执行所有检查后的报告状态。它将是以下之一。

  • ok:所有检查均已成功通过。
  • warning:一个或多个检查报告了警告。
  • error:一个或多个检查失败。

finishedAt

测试完成的日期时间。

checks

一个对象数组,包含所有已执行检查的详细报告。

debugInfo

调试信息可用于识别进程及其运行时长。它包含以下属性。

  • pid:进程 ID。
  • ppid:管理你的 AdonisJS 应用进程的父进程的进程 ID。
  • platform:应用运行所在的平台。
  • uptime:应用已运行的时长(以秒为单位)。
  • version:Node.js 版本。

保护该端点

你可以使用 auth 中间件,或创建一个检查请求头中特定 API 密钥的自定义中间件,来保护 /health 端点免受公开访问。例如:

import router from '@adonisjs/core/services/router'
const HealthChecksController = () => import('#controllers/health_checks_controller')
router
.get('/health', [HealthChecksController])
.use(({ request, response }, next) => {
if (request.header('x-monitoring-secret') === 'some_secret_value') {
return next()
}
response.unauthorized({ message: 'Unauthorized access' })
})

可用的健康检查

以下是你可以在 start/health.ts 文件中配置的可用健康检查列表。

DiskSpaceCheck

DiskSpaceCheck 会计算服务器上已用的磁盘空间,并在超过某个阈值时报告警告/错误。

import { HealthChecks, DiskSpaceCheck } from '@adonisjs/core/health'
export const healthChecks = new HealthChecks().register([
new DiskSpaceCheck()
])

默认情况下,警告阈值设置为 75%,失败阈值设置为 80%。不过,你也可以定义自定义阈值。

export const healthChecks = new HealthChecks().register([
new DiskSpaceCheck()
.warnWhenExceeds(80) // warn when used over 80%
.failWhenExceeds(90), // fail when used over 90%
])

MemoryHeapCheck

MemoryHeapCheck 会监控由 process.memoryUsage() 方法报告的堆大小,并在超过某个阈值时报告警告/错误。

import { HealthChecks, MemoryHeapCheck } from '@adonisjs/core/health'
export const healthChecks = new HealthChecks().register([
new MemoryHeapCheck()
])

默认情况下,警告阈值设置为 250MB,失败阈值设置为 300MB。不过,你也可以定义自定义阈值。

export const healthChecks = new HealthChecks().register([
new MemoryHeapCheck()
.warnWhenExceeds('300 mb')
.failWhenExceeds('700 mb'),
])

MemoryRSSCheck

MemoryRSSCheck 会监控由 process.memoryUsage() 方法报告的常驻集大小(Resident Set Size),并在超过某个阈值时报告警告/错误。

import { HealthChecks, MemoryRSSCheck } from '@adonisjs/core/health'
export const healthChecks = new HealthChecks().register([
new MemoryRSSCheck()
])

默认情况下,警告阈值设置为 320MB,失败阈值设置为 350MB。不过,你也可以定义自定义阈值。

export const healthChecks = new HealthChecks().register([
new MemoryRSSCheck()
.warnWhenExceeds('600 mb')
.failWhenExceeds('800 mb'),
])

DbCheck

DbCheck@adonisjs/lucid 包提供,用于监控与 SQL 数据库的连接。你可以按如下方式导入并使用它。

import db from '@adonisjs/lucid/services/db'
import { DbCheck } from '@adonisjs/lucid/database'
import { HealthChecks, DiskSpaceCheck, MemoryHeapCheck } from '@adonisjs/core/health'
export const healthChecks = new HealthChecks().register([
new DiskSpaceCheck(),
new MemoryHeapCheck(),
new DbCheck(db.connection()),
])

以下是数据库健康检查的一个示例报告。

Report sample
{
"name": "Database health check (postgres)",
"isCached": false,
"message": "Successfully connected to the database server",
"status": "ok",
"finishedAt": "2024-06-20T07:18:23.830Z",
"meta": {
"connection": {
"name": "postgres",
"dialect": "postgres"
}
}
}

DbCheck 类接受一个用于监控的数据库连接。如果你想监控多个连接,请为每个连接多次注册此检查。例如:

Monitoring multiple connections
export const healthChecks = new HealthChecks().register([
new DiskSpaceCheck(),
new MemoryHeapCheck(),
new DbCheck(db.connection()),
new DbCheck(db.connection('mysql')),
new DbCheck(db.connection('pg')),
])

DbConnectionCountCheck

DbConnectionCountCheck 会监控数据库服务器上的活动连接,并在超过某个阈值时报告警告/错误。此检查仅支持 PostgreSQLMySQL 数据库。

import db from '@adonisjs/lucid/services/db'
import { DbCheck, DbConnectionCountCheck } from '@adonisjs/lucid/database'
import { HealthChecks, DiskSpaceCheck, MemoryHeapCheck } from '@adonisjs/core/health'
export const healthChecks = new HealthChecks().register([
new DiskSpaceCheck(),
new MemoryHeapCheck(),
new DbCheck(db.connection()),
new DbConnectionCountCheck(db.connection())
])

以下是数据库连接数健康检查的一个示例报告。

Report sample
{
"name": "Connection count health check (postgres)",
"isCached": false,
"message": "There are 6 active connections, which is under the defined thresholds",
"status": "ok",
"finishedAt": "2024-06-20T07:30:15.840Z",
"meta": {
"connection": {
"name": "postgres",
"dialect": "postgres"
},
"connectionsCount": {
"active": 6,
"warningThreshold": 10,
"failureThreshold": 15
}
}
}

默认情况下,警告阈值设置为 10 个连接,失败阈值设置为 15 个连接。不过,你也可以定义自定义阈值。

new DbConnectionCountCheck(db.connection())
.warnWhenExceeds(4)
.failWhenExceeds(10)

RedisCheck

RedisCheck@adonisjs/redis 包提供,用于监控与 Redis 数据库(包括 Cluster)的连接。你可以按如下方式导入并使用它。

import redis from '@adonisjs/redis/services/main'
import { RedisCheck } from '@adonisjs/redis'
import { HealthChecks, DiskSpaceCheck, MemoryHeapCheck } from '@adonisjs/core/health'
export const healthChecks = new HealthChecks().register([
new DiskSpaceCheck(),
new MemoryHeapCheck(),
new RedisCheck(redis.connection()),
])

以下是数据库健康检查的一个示例报告。

Report sample
{
"name": "Redis health check (main)",
"isCached": false,
"message": "Successfully connected to the redis server",
"status": "ok",
"finishedAt": "2024-06-22T05:37:11.718Z",
"meta": {
"connection": {
"name": "main",
"status": "ready"
}
}
}

RedisCheck 类接受一个用于监控的 redis 连接。如果你想监控多个连接,请为每个连接多次注册此检查。例如:

Monitoring multiple connections
export const healthChecks = new HealthChecks().register([
new DiskSpaceCheck(),
new MemoryHeapCheck(),
new RedisCheck(redis.connection()),
new RedisCheck(redis.connection('cache')),
new RedisCheck(redis.connection('locks')),
])

RedisMemoryUsageCheck

RedisMemoryUsageCheck 会监控 redis 服务器的内存消耗,并在超过某个阈值时报告警告/错误。

import redis from '@adonisjs/redis/services/main'
import { RedisCheck, RedisMemoryUsageCheck } from '@adonisjs/redis'
import { HealthChecks, DiskSpaceCheck, MemoryHeapCheck } from '@adonisjs/core/health'
export const healthChecks = new HealthChecks().register([
new DiskSpaceCheck(),
new MemoryHeapCheck(),
new RedisCheck(redis.connection()),
new RedisMemoryUsageCheck(redis.connection())
])

以下是 redis 内存使用健康检查的一个示例报告。

Report sample
{
"name": "Redis memory consumption health check (main)",
"isCached": false,
"message": "Redis memory usage is 1.06MB, which is under the defined thresholds",
"status": "ok",
"finishedAt": "2024-06-22T05:36:32.524Z",
"meta": {
"connection": {
"name": "main",
"status": "ready"
},
"memoryInBytes": {
"used": 1109616,
"warningThreshold": 104857600,
"failureThreshold": 125829120
}
}
}

默认情况下,警告阈值设置为 100MB,失败阈值设置为 120MB。不过,你也可以定义自定义阈值。

new RedisMemoryUsageCheck(db.connection())
.warnWhenExceeds('200MB')
.failWhenExceeds('240MB')

缓存结果

每次调用 healthChecks.run 方法(即访问 /health 端点)时都会执行健康检查。你可能希望频繁地 ping /health 端点,但又想避免在每次访问时都执行某些检查。

例如,每分钟监控一次磁盘空间不是很有用,但每分钟跟踪一次内存则可能会有帮助。

因此,我们允许你在注册各个健康检查时缓存它们的结果。例如:

import {
HealthChecks,
MemoryRSSCheck,
DiskSpaceCheck,
MemoryHeapCheck,
} from '@adonisjs/core/health'
export const healthChecks = new HealthChecks().register([
new DiskSpaceCheck().cacheFor('1 hour'),
new MemoryHeapCheck(), // do not cache
new MemoryRSSCheck(), // do not cache
])

创建自定义健康检查

你可以将自定义健康检查创建为一个遵循 HealthCheckContract 接口的 JavaScript 类。你可以在项目或包内的任何地方定义健康检查,并在 start/health.ts 文件中导入以注册它。

import { Result, BaseCheck } from '@adonisjs/core/health'
import type { HealthCheckResult } from '@adonisjs/core/types/health'
export class ExampleCheck extends BaseCheck {
async run(): Promise<HealthCheckResult> {
/**
* The following checks are for reference purposes only
*/
if (checkPassed) {
return Result.ok('Success message to display')
}
if (checkFailed) {
return Result.failed('Error message', errorIfAny)
}
if (hasWarning) {
return Result.warning('Warning message')
}
}
}

如上例所示,你可以使用 Result 类来创建健康检查结果。你也可以选择性地为结果合并元数据,如下所示。

Result.ok('Database connection is healthy').mergeMetaData({
connection: {
dialect: 'pg',
activeCount: connections,
},
})

注册自定义健康检查

你可以在 start/health.ts 文件中导入你的自定义健康检查类,并通过创建一个新的类实例来注册它。

import { ExampleCheck } from '../app/health_checks/example.js'
export const healthChecks = new HealthChecks().register([
new DiskSpaceCheck().cacheFor('1 hour'),
new MemoryHeapCheck(),
new MemoryRSSCheck(),
new ExampleCheck()
])