Prometheus 监控 Node.js 应用终极实战:prom-client 原生集成与全栈可观测性
Node.js 凭借事件驱动和非阻塞 I/O,支撑着大量高并发 Web 服务、API 网关与实时应用。然而,事件循环延迟(event loop lag)、内存泄漏、GC 停顿、未处理的 Promise 拒绝、HTTP 错误率攀升,若缺乏量化监控,往往在造成雪崩后才被发现。prom-client 是 Node.js 生态中 Prometheus 官方推荐的客户端库,它能够以极低的侵入性,将 Node.js 运行时指标、进程指标、HTTP 请求指标以及自定义业务指标,以标准 Prometheus 格式暴露。本文将带你从零集成 prom-client、配置 /metrics 端点,到构建 Grafana 仪表盘与告警规则,让 Node.js 服务内部状态完全透明。
1. 为什么选择 prom-client?
- 官方支持:Prometheus 生态中 Node.js 的首选客户端,长期维护。
- 内置指标丰富:自动采集事件循环延迟、GC、内存、堆栈、句柄、HTTP 请求时长等。
- 低开销:基于原生 C++ 绑定获取 V8 堆与 GC 统计,性能影响可忽略。
- 完美适配多进程:支持 cluster 模块和进程管理器(PM2),可聚合多 Worker 指标。
- 类型安全:提供完整的 TypeScript 类型定义。
2. 快速集成:安装与基本端点
2.1 安装
npm install prom-client
2.2 创建最简 HTTP 服务器并暴露 /metrics
const http = require('http');
const client = require('prom-client');
// 创建 Registry(不推荐直接使用全局,但单进程简单可用)
const register = new client.Registry();
// 启用默认指标(包括 process、eventLoop、GC 等)
client.collectDefaultMetrics({ register });
const server = http.createServer(async (req, res) => {
if (req.url === '/metrics') {
res.setHeader('Content-Type', register.contentType);
res.end(await register.metrics());
} else {
res.end('Hello World');
}
});
server.listen(8080, () => console.log('Listening on :8080'));
访问 http://localhost:8080/metrics,你会看到 nodejs_eventloop_lag_seconds、process_cpu_user_seconds_total、nodejs_heap_size_used_bytes 等指标。
2.3 在 Express 中使用
const express = require('express');
const client = require('prom-client');
const app = express();
const register = new client.Registry();
client.collectDefaultMetrics({ register });
app.get('/metrics', async (req, res) => {
res.set('Content-Type', register.contentType);
res.end(await register.metrics());
});
app.listen(3000);
3. 内置指标深度解读
collectDefaultMetrics() 将自动采集以下类别的指标,前缀各异。
3.1 事件循环延迟
| nodejs_eventloop_lag_seconds (Histogram) | 事件循环延迟分布,反映 Node.js 主线程被阻塞的程度 |
PromQL:
- P99 事件循环延迟:histogram_quantile(0.99, rate(nodejs_eventloop_lag_seconds_bucket[5m]))
- 高延迟意味着 CPU 密集型任务或同步阻塞过多。
3.2 内存与 V8 堆
| nodejs_heap_size_total_bytes | V8 堆总大小(含已用和空闲) |
| nodejs_heap_size_used_bytes | 实际使用的堆内存 |
| nodejs_external_memory_bytes | 绑定到 JavaScript 的 C++ 对象占用的内存 |
| nodejs_heap_spaces_size_total_bytes{space="read_only_space"} | 各内存空间大小 |
PromQL:
- 堆内存使用率:nodejs_heap_size_used_bytes / nodejs_heap_size_total_bytes * 100
3.3 GC(垃圾回收)
| nodejs_gc_duration_seconds (Histogram) | 各类型 GC 耗时分布(Scavenge、MarkSweepCompact 等) |
PromQL:
- GC P99 暂停:histogram_quantile(0.99, rate(nodejs_gc_duration_seconds_bucket{kind="all"}[5m]))
3.4 堆栈与句柄
| nodejs_active_handles | 活动句柄数(定时器、服务器等) |
| nodejs_active_handles_total | 累计分配的句柄总数 |
| nodejs_active_requests | 活动请求数 |
句柄泄漏(持续增长不释放)是内存泄漏的常见原因。
3.5 进程指标
| process_cpu_user_seconds_total / process_cpu_system_seconds_total | 用户态/系统态 CPU 时间 |
| process_cpu_seconds_total | 总 CPU 时间 |
| process_resident_memory_bytes | 常驻内存(RSS) |
| process_open_fds | 打开的文件描述符数 |
| process_max_fds | 最大文件描述符数 |
| process_start_time_seconds | 进程启动时间 |
PromQL:
- CPU 使用率:rate(process_cpu_seconds_total[5m]) * 100
- 文件描述符使用率:process_open_fds / process_max_fds * 100
4. 自定义业务指标
prom-client 提供 Counter、Gauge、Histogram、Summary 四种类型。
4.1 Counter
const httpRequestsTotal = new client.Counter({
name: 'http_requests_total',
help: 'Total number of HTTP requests',
labelNames: ['method', 'route', 'status_code'],
});
// 中间件使用
app.use((req, res, next) => {
res.on('finish', () => {
httpRequestsTotal.inc({ method: req.method, route: req.route?.path || '/', status_code: res.statusCode });
});
next();
});
4.2 Histogram(请求延迟)
const httpRequestDuration = new client.Histogram({
name: 'http_request_duration_seconds',
help: 'Duration of HTTP requests in seconds',
labelNames: ['method', 'route'],
buckets: [0.01, 0.05, 0.1, 0.5, 1, 2, 5, 10],
});
// 计时
app.use((req, res, next) => {
const end = httpRequestDuration.startTimer({ method: req.method, route: req.route?.path || '/' });
res.on('finish', end);
next();
});
4.3 Gauge
const activeRequests = new client.Gauge({
name: 'active_requests',
help: 'Number of active requests',
});
app.use((req, res, next) => {
activeRequests.inc();
res.on('finish', () => activeRequests.dec());
next();
});
5. 多进程(cluster)指标聚合
Node.js 常以 cluster 模式或 PM2 启动多个工作进程。默认情况下每个进程暴露自己的 /metrics 端点,需进行聚合。
方案: 使用 prom-client 的 聚合模式,在主进程中收集各 Worker 的指标,并统一暴露一个端点。
const cluster = require('cluster');
const client = require('prom-client');
if (cluster.isMaster) {
// 主进程:聚合所有 worker 的指标
const AggregatorRegistry = client.AggregatorRegistry;
const aggregatorRegistry = new AggregatorRegistry();
// 启动聚合服务器
require('http').createServer(async (req, res) => {
if (req.url === '/metrics') {
res.setHeader('Content-Type', aggregatorRegistry.contentType);
res.end(await aggregatorRegistry.metrics());
}
}).listen(9090);
} else {
// 工作进程:只注册默认指标,暴露在随机端口(但不会直接对外)
const register = new client.Registry();
client.collectDefaultMetrics({ register });
// 可以继续启动应用服务器监听业务端口…
}
也可以使用外部聚合工具如 node_exporter 的 textfile 收集每个进程的指标文件,但推荐内置的聚合方案。
6. 配置 Prometheus 抓取
scrape_configs:
– job_name: 'nodejs-app'
scrape_interval: 15s
static_configs:
– targets: ['node-app:8080'] # 或聚合端口 9090
labels:
app: 'api-gateway'
env: 'production'
7. Grafana 仪表盘推荐
- Node.js Dashboard for Prometheus:ID 11159,展示事件循环延迟、堆内存、GC、CPU、HTTP 请求速率等,适配 prom-client 默认指标。
- Node.js Application Dashboard:ID 12231,更多聚焦 HTTP 和业务指标。
- PM2 Dashboard:ID 11170(若使用 PM2 管理进程)。
- 自定义面板:可创建订单处理速率、支付延迟百分位等业务视图。
导入后选择数据源,使用 app 变量过滤。
8. 告警规则实战
groups:
– name: nodejs_alerts
rules:
– alert: NodejsAppDown
expr: up{job="nodejs–app"} == 0
for: 1m
labels:
severity: critical
annotations:
summary: "Node.js 应用 {{ $labels.instance }} 不可达"
– alert: NodejsHighEventLoopLag
expr: histogram_quantile(0.99, rate(nodejs_eventloop_lag_seconds_bucket[5m])) > 0.05
for: 5m
labels:
severity: warning
annotations:
summary: "事件循环 P99 延迟超过 50ms,主线程可能阻塞"
– alert: NodejsHighHeapUsage
expr: nodejs_heap_size_used_bytes / nodejs_heap_size_total_bytes > 0.9
for: 10m
labels:
severity: warning
annotations:
summary: "堆内存使用率超过 90%"
– alert: NodejsHighGCPause
expr: histogram_quantile(0.99, rate(nodejs_gc_duration_seconds_bucket[5m])) > 0.1
for: 5m
labels:
severity: warning
annotations:
summary: "GC P99 暂停超过 100ms"
– alert: NodejsHighFileDescriptors
expr: process_open_fds / process_max_fds > 0.8
for: 5m
labels:
severity: warning
annotations:
summary: "文件描述符使用率超过 80%"
– alert: NodejsHttp5xxRate
expr: rate(http_requests_total{status_code=~"5.."}[5m]) / rate(http_requests_total[5m]) > 0.01
for: 5m
labels:
severity: critical
annotations:
summary: "HTTP 5xx 错误率超过 1%"
– alert: NodejsHighLatency
expr: histogram_quantile(0.99, rate(http_request_duration_seconds_bucket[5m])) > 2
for: 5m
labels:
severity: warning
annotations:
summary: "HTTP 请求 P99 延迟超过 2 秒"
9. 进阶:安全、多服务与性能优化
9.1 安全加固
- 将 /metrics 端口绑定至内网 IP 或使用独立端口(如 9090),避免暴露到公网。
- 可使用 Express 中间件添加简单 Token 校验:
app.use('/metrics', (req, res, next) => {
if (req.headers.authorization === 'Bearer my-secret') next();
else res.status(401).end();
});
- Prometheus 侧配置 basic_auth 或通过反向代理实现。
9.2 多服务监控
每个服务独立暴露端点,Prometheus 通过 app 标签区分。可结合服务发现(如 Kubernetes Pod Annotations)。
9.3 减少指标基数
- 不要在标签中使用用户 ID、订单号等动态值。
- 谨慎设置 Histogram 的 buckets 数量,一般 10~15 个足够。
- 定期检查 nodejs_active_handles 判断是否泄漏。
9.4 TypeScript 支持
prom-client 包含 TypeScript 类型定义,可直接使用:
import { Counter, Histogram } from 'prom-client';
10. 总结
通过 prom-client,Node.js 应用的事件循环延迟、堆内存分配、GC 停顿、HTTP 吞吐等关键健康指标,全部转化为 Prometheus 可查询、可告警的时序数据。无论你的 Node.js 服务是简单的 API、实时 WebSocket 网关还是集群化微服务,这套方案都能无痛接入,让异步回调世界里的性能瓶颈和内存泄漏无处遁形。结合 Grafana 仪表盘和 Alertmanager 及时通知,将故障扼杀在萌芽,为高并发 Node.js 应用构建坚实的可观测性底座。



