健康检查
本指南介绍 AdonisJS 应用中的健康检查。你将学到如何:
- 理解存活探针(liveness)与就绪探针(readiness)之间的区别
- 使用内置的设置命令配置健康检查
- 为监控服务和编排器暴露端点
- 使用针对磁盘空间、内存、数据库和 Redis 的内置检查
- 缓存健康检查结果以优化性能
- 为应用特定需求创建自定义健康检查
概述
健康检查允许你的应用向外部系统(如负载均衡器、容器编排器(Kubernetes、Docker Swarm)和监控服务)报告其运行状态。这些系统会定期探测你的应用,以判断它应接收流量还是应被重启。
AdonisJS 在核心框架中内置了一套健康检查系统,并针对常见的基础设施关注点提供了若干现成的检查。你也可以为应用特定需求创建自定义检查,例如验证外部 API 连通性或队列连接。
存活探针 vs 就绪探针
在实现健康检查之前,理解这两种探针以及各自何时使用非常重要。
**存活探针(liveness probe)**回答的问题是:“进程是否存活且能响应?”这是一个简单的检查,用于验证你的应用能否响应 HTTP 请求。如果存活探针反复失败,编排器将重启该容器。存活探针应保持轻量,避免检查外部依赖,因为数据库故障不应导致你的应用陷入重启循环。
**就绪探针(readiness probe)**回答的问题是:“应用是否准备好接收流量?”此检查验证你的应用及其依赖(数据库连接、Redis、外部服务)是否正常工作。如果就绪探针失败,编排器会将该实例从负载均衡器中移除,但不会重启它。这让应用有时间恢复,例如当数据库连接暂时不可用时。
| 探针 | 目的 | 失败时 | 是否应检查依赖 |
|---|---|---|---|
| 存活 | 进程是否存活? | 重启容器 | 否 |
| 就绪 | 能否处理请求? | 从负载均衡器移除 | 是 |
配置健康检查
运行以下命令在你的应用中设置健康检查。这会创建配置文件以及一个同时带有存活和就绪端点的控制器。
node ace configure health_checks
该命令会创建两个文件。第一个是健康检查配置,你在其中注册要运行的检查。
import { HealthChecks, DiskSpaceCheck, MemoryHeapCheck } from '@adonisjs/core/health'
export const healthChecks = new HealthChecks().register([
new DiskSpaceCheck(),
new MemoryHeapCheck(),
])
第二个是暴露两个探针端点的控制器。
import { healthChecks } from '#start/health'
import type { HttpContext } from '@adonisjs/core/http'
export default class HealthChecksController {
/**
* Liveness probe: Returns 200 if the process is running.
* Does not check dependencies.
*/
async live({ response }: HttpContext) {
return response.ok()
}
/**
* Readiness probe: Runs all registered health checks
* and returns the detailed report.
*/
async ready({ response }: HttpContext) {
const report = await healthChecks.run()
if (report.isHealthy) {
return response.ok(report)
}
return response.serviceUnavailable(report)
}
}
存活方法只是返回一个 200 状态码,证明进程存活且能处理 HTTP 请求。就绪方法运行所有已注册的健康检查,健康时返回 200,任一检查失败时返回 503(服务不可用)。
暴露端点
在你的路由文件中注册健康检查路由。为每个探针使用单独的路径,可让编排器独立配置它们。
import router from '@adonisjs/core/services/router'
import { controllers } from '#generated/controllers'
router.get('/health/live', [controllers.HealthChecks, 'live'])
router.get('/health/ready', [controllers.HealthChecks, 'ready'])
有了这些路由,你的监控系统就可以探测 /health/live 进行存活检查,探测 /health/ready 进行就绪检查。
理解就绪报告
就绪端点返回一份详细的 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
检查完成时的时间戳。
debugInfo
进程信息,包括 PID、平台、以秒为单位的运行时间,以及 Node.js 版本。
checks
包含每项已注册检查详细结果的数组。
checks 数组中的每项检查都包含其名称、状态、消息、结果是否来自缓存,以及该检查类型特有的任何元数据。
保护就绪端点
就绪报告包含关于你基础设施的详细信息,你可能不希望将其公开暴露。你可以使用一个密钥头部来保护该端点,你的监控系统会在每个请求中带上它。
Kubernetes 和大多数监控工具都支持在探针请求上使用自定义 HTTP 头部,因此你可以配置它们带上你的密钥。
import router from '@adonisjs/core/services/router'
const HealthChecksController = () => import('#controllers/health_checks_controller')
router.get('/health/live', [HealthChecksController, 'live'])
router
.get('/health/ready', [HealthChecksController, 'ready'])
.use(({ request, response }, next) => {
if (request.header('x-monitoring-secret') === 'some_secret_value') {
return next()
}
return response.unauthorized({ message: 'Unauthorized access' })
})
在 Kubernetes 中,配置探针使其带上该头部。
readinessProbe:
httpGet:
path: /health/ready
port: 3333
httpHeaders:
- name: x-monitoring-secret
value: some_secret_value
可用的健康检查
AdonisJS 提供了若干内置的健康检查,你可以在 start/health.ts 文件中注册它们。
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)
.failWhenExceeds(90),
])
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
监控由 process.memoryUsage() 报告的 RSS(常驻集大小,即为进程分配的总内存)。
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
由 @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()),
])
一次成功的检查会返回如下报告。
{
"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"
}
}
}
要监控多个数据库连接,请为每个连接各注册一次该检查。
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
监控活跃的数据库连接数,并在数量超过阈值时告警。此检查仅支持 PostgreSQL 和 MySQL 数据库。
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()),
])
默认警告阈值为 10 个连接,失败阈值为 15 个连接。
new DbConnectionCountCheck(db.connection())
.warnWhenExceeds(4)
.failWhenExceeds(10)
RedisCheck
由 @adonisjs/redis 包提供,此检查验证与你 Redis 服务器(包括集群配置)的连通性。
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()),
])
要监控多个 Redis 连接,请为每个连接各注册一次该检查。
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
监控 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()),
])
默认警告阈值为 100MB,失败阈值为 120MB。
new RedisMemoryUsageCheck(redis.connection())
.warnWhenExceeds('200 mb')
.failWhenExceeds('240 mb')
缓存结果
每次调用就绪端点时都会运行健康检查。对于不需要每次请求都运行的检查(如变化缓慢的磁盘空间),你可以将结果缓存指定的时长。
import {
HealthChecks,
MemoryRSSCheck,
DiskSpaceCheck,
MemoryHeapCheck,
} from '@adonisjs/core/health'
export const healthChecks = new HealthChecks().register([
new DiskSpaceCheck().cacheFor('1 hour'),
new MemoryHeapCheck(),
new MemoryRSSCheck(),
])
在本示例中,磁盘空间检查每小时最多运行一次,对后续请求返回缓存的结果。由于内存使用可能快速变化,内存检查在每个请求上都会运行。
当某个结果来自缓存时,该检查报告中的 isCached 属性将为 true。
创建自定义健康检查
你可以创建自定义健康检查,以验证应用特定需求,如外部 API 连通性、消息队列连接,或业务关键服务的可用性。
自定义健康检查是一个继承 BaseCheck 并实现 run 方法的类。你可以将自定义检查放在项目的任何位置,不过 app/health_checks/ 是一个合理的位置。
import { Result, BaseCheck } from '@adonisjs/core/health'
import type { HealthCheckResult } from '@adonisjs/core/types/health'
export class PaymentGatewayCheck extends BaseCheck {
async run(): Promise<HealthCheckResult> {
/**
* Attempt to reach the payment gateway's health endpoint.
* In a real implementation, you would make an HTTP request
* to verify the service is reachable.
*/
const isReachable = await this.checkGatewayConnectivity()
if (!isReachable) {
return Result.failed('Payment gateway is unreachable')
}
const latency = await this.measureLatency()
if (latency > 2000) {
return Result.warning('Payment gateway latency is high').mergeMetaData({
latencyMs: latency,
})
}
return Result.ok('Payment gateway is healthy').mergeMetaData({
latencyMs: latency,
})
}
protected async checkGatewayConnectivity(): Promise<boolean> {
// Implementation here
return true
}
protected async measureLatency(): Promise<number> {
// Implementation here
return 150
}
}
Result 类提供了三个用于创建健康检查结果的工厂方法:
| 方法 | 用途 |
|---|---|
Result.ok(message) | 检查成功通过 |
Result.warning(message) | 检查通过但存在隐患 |
Result.failed(message, error?) | 检查失败,可选地包含错误 |
使用 mergeMetaData 方法在检查报告中包含额外信息,如延迟测量值、连接数或版本号。
注册自定义健康检查
在 start/health.ts 中导入你的自定义检查,并与其他检查一起注册。
import { HealthChecks, DiskSpaceCheck, MemoryHeapCheck } from '@adonisjs/core/health'
import { PaymentGatewayCheck } from '#health_checks/payment_gateway_check'
export const healthChecks = new HealthChecks().register([
new DiskSpaceCheck().cacheFor('1 hour'),
new MemoryHeapCheck(),
new PaymentGatewayCheck(),
])
现在,每当调用就绪端点时,你的自定义检查都会与内置检查一起运行。