Article

系统监控 Prometheus

更新于:2026-07-14

第 1 章 概述与核心概念

1.1 什么是 Prometheus

概念名称说明注意事项
Prometheus开源的系统监控和警报工具包,由 SoundCloud 开发并于 2012 年开源,现为 CNCF 项目。支持多维数据模型和强大的查询语言 PromQL。主要适用于时间序列数据的采集、存储与告警,不适用于日志聚合或追踪(需配合其他系统如 Loki、Jaeger)。
时间序列数据按时间顺序记录的指标值序列,每个序列由指标名和一组标签(key=value)唯一标识。数据具有高写入频率、不可变性、按时间索引的特点。
PromQL (Prometheus Query Language)Prometheus 的查询语言,用于对时间序列数据执行选择和聚合操作,支持算术、函数、逻辑运算等。初学者需掌握基本语法如 rate()increase()histogram_quantile() 等常用函数。

1.2 Prometheus 在 Java 应用监控中的角色

概念名称说明注意事项
监控目标Prometheus 通过 HTTP 协议定期从 Java 应用暴露的 /metrics 端点拉取监控数据。需确保 Java 应用启动了 HTTP 服务并正确暴露指标端点。
客户端库支持Java 应用使用 prometheus-client 库来创建和管理指标,并提供标准格式的指标输出。推荐使用官方客户端库以保证兼容性和稳定性。
实时可观测性提供方法调用次数、响应延迟、错误率、JVM 状态等运行时信息,提升故障排查效率。不应过度暴露敏感业务数据作为指标,避免信息泄露。
警报集成结合 Alertmanager 可基于 Java 应用指标设置阈值触发警报(如异常率突增)。警报规则应在 Prometheus Server 中配置,而非 Java 应用内。

1.3 核心数据模型:Metrics 类型详解

指标类型说明注意事项
Counter累积计数器,仅能递增或保持不变,常用于请求数、错误数等累计统计。重启后重置为 0,Prometheus 使用 rate()increase() 函数处理重置问题。
Gauge可任意增减的瞬时值,适合表示温度、内存使用量、当前在线用户数等。可用于记录可上升也可下降的数值,无需担心单调性。
Histogram对观测值(如请求延迟)进行采样并分桶统计,生成多个时间序列:样本总数、总和及各区间计数。默认产生 _count_sum_bucket{le="x"} 多个时间序列;适合计算分位数。
Summary类似 Histogram,但直接在客户端计算分位数(如 95%, 99%),生成 _count_sum_quantile 序列。分位数计算在客户端完成,受采样窗口影响,不如 Histogram 灵活。
Untyped表示类型未知的指标,用于无法预先确定类型的场景。少见用途,一般应明确指定指标类型以获得更好语义支持。

1.4 拉模型(Pull Model)与服务发现

概念名称说明注意事项
拉模型 (Pull Model)Prometheus Server 主动通过 HTTP 协议周期性地从被监控目标拉取 /metrics 数据。要求目标服务必须持续可用且暴露 HTTP 接口;网络防火墙需允许访问。
推模型对比传统监控系统(如 StatsD)采用客户端主动推送方式。Push 模式适合短暂生命周期任务,Pull 模式更适合长期运行服务。
服务发现 (Service Discovery)Prometheus 支持自动发现监控目标,无需手动维护静态列表。支持 Kubernetes、Consul、DNS 等多种机制。动态环境下推荐启用服务发现以减少运维负担。
Scraping Interval指定 Prometheus 拉取指标的频率,默认通常为 15 秒或 30 秒。设置过短会增加系统负载,过长则降低监控实时性。

1.5 Prometheus 生态组件简介(Prometheus Server, Alertmanager, Grafana)

组件名称说明注意事项
Prometheus Server核心组件,负责抓取、存储时间序列数据,提供 PromQL 查询接口。需合理配置 retention period 和 storage backend(本地或远程)。
Alertmanager处理由 Prometheus Server 发出的警报,支持去重、分组、静默、通知路由等功能。不直接接收指标,只处理告警事件;可集成邮件、Slack、Webhook 等通知渠道。
Grafana开源可视化平台,支持连接 Prometheus 作为数据源,构建丰富的仪表盘。推荐用于展示 Java 应用的关键性能指标(KPIs)。
Exporters用于将第三方系统(如 MySQL、Node.js、JMX)的指标转化为 Prometheus 格式。Java 应用可通过 jmx_exporter 暴露 JVM 底层指标。
Pushgateway允许短期作业将指标推送到中间网关,供 Prometheus 拉取。解决无法持久暴露 /metrics 端点的问题;避免滥用以防数据堆积。

第 2 章 Java 集成 Prometheus 基础

2.1 引入 Prometheus 客户端库(prometheus-client)

方法/依赖项语法用途代码示例注意事项
Maven 依赖添加<dependency> <groupId>io.prometheus</groupId> <artifactId>simpleclient</artifactId> <version>0.16.0</version> </dependency>引入核心客户端库io.prometheus simpleclient 0.16.0版本号应根据实际需求选择最新稳定版;注意与其他依赖兼容性。
simpleclient_httpserversimpleclient_httpserver提供嵌入式 HTTP 服务器用于暴露 /metrics 端点io.prometheus simpleclient_httpserver 0.16.0必须引入此模块才能启动 HTTP 服务暴露指标。
simpleclient_hotspotsimpleclient_hotspot提供对 JVM 内部指标(GC、内存池、线程等)的支持io.prometheus simpleclient_hotspot 0.16.0用于收集 JVM 相关指标,建议生产环境启用。
simpleclient_commonsimpleclient_common包含通用工具类和基础度量类型抽象自动随核心库引入一般无需单独声明依赖。

2.2 启动一个简单的 HTTP 服务暴露指标

方法名称语法用途代码示例注意事项
HTTPServernew HTTPServer(int port)启动一个监听指定端口的 HTTP 服务,用于暴露 /metrics 端点HTTPServer server = new HTTPServer(8080);确保端口未被占用;建议使用非 80/443 的专用监控端口。
HTTPServernew HTTPServer(InetSocketAddress addr, boolean daemon)指定绑定地址和是否为守护线程启动服务InetSocketAddress addr = new InetSocketAddress("localhost", 8080); HTTPServer server = new HTTPServer(addr, true);daemon 设为 true 可防止主线程退出时阻塞 JVM 关闭。
CollectorRegistry.defaultRegistrystatic final CollectorRegistry defaultRegistry默认的指标注册表,所有指标默认注册于此CollectorRegistry registry = CollectorRegistry.defaultRegistry;可自定义 registry,但多数情况使用默认即可。
MetricsServletnew MetricsServlet()用于 Servlet 容器中暴露指标(替代独立 HTTPServer)ServletContextHandler context = new ServletContextHandler(); context.addServlet(new ServletHolder(new MetricsServlet()), "/metrics");适用于嵌入 Jetty 等 Web 服务器场景。

2.3 注册自定义指标的基本流程

步骤说明注意事项
1. 创建指标实例使用 Counter.build()Gauge.build() 等工厂方法构建指标对象必须调用 .register() 才能生效。
2. 设置名称与帮助文本使用 .name("my_counter").help("description")名称应符合命名规范(字母、数字、下划线,小写开头)。
3. 注册到 CollectorRegistry调用 .register() 方法将指标注册到 registry默认注册到 defaultRegistry;可传参指定其他 registry。
4. 更新指标值调用 inc()set()observe() 等方法更新值确保线程安全,尤其在并发环境中。
5. 暴露 HTTP 端点启动 HTTPServer 或部署 MetricsServlet客户端库会自动将注册表中的指标序列化为文本格式返回。

2.4 使用默认导出器(DefaultExports)收集 JVM 指标

方法名称语法用途代码示例注意事项
DefaultExports.initialize()public static void initialize()注册所有默认的 JVM 相关指标收集器DefaultExports.initialize();应在应用启动早期调用一次即可。
StandardExportsnew StandardExports().register()导出 JVM 基本信息(如版本、类加载数)new StandardExports().register();已包含在 DefaultExports 中,无需重复调用。
MemoryPoolsExportsnew MemoryPoolsExports().register()导出各内存池(Eden, Survivor, Old Gen 等)使用情况new MemoryPoolsExports().register();可观察 GC 行为和内存压力。
GarbageCollectorExportsnew GarbageCollectorExports().register()导出 GC 次数与耗时指标new GarbageCollectorExports().register();关键性能诊断指标来源。
ThreadExportsnew ThreadExports().register()导出线程数量(总线程数、守护线程数等)new ThreadExports().register();有助于发现线程泄漏问题。
FileDescriptorExportsnew FileDescriptorExports().register()导出文件描述符使用数量(仅 Linux/Unix)new FileDescriptorExports().register();防止达到系统限制导致连接失败。
BufferPoolExportsnew BufferPoolExports().register()导出 NIO 缓冲池(direct, mapped)使用情况new BufferPoolExports().register();适用于大量 I/O 操作的应用。

第 3 章 核心指标类型与使用

3.1 Counter(计数器)

方法名称语法用途代码示例注意事项
buildCounter.build()创建一个 Counter 构建器实例Counter counter = Counter.build();必须调用 register() 才能生效。
name.name(String name)设置指标名称.name("requests_total")名称只能包含字母、数字、下划线,建议小写开头。
help.help(String help)设置帮助文本(描述).help("Total number of HTTP requests")建议提供清晰说明以便理解指标含义。
labelNames.labelNames(String... labelNames)定义标签名(维度).labelNames("method", "status")标签名用于后续区分不同维度的数据。
register.register()注册指标到默认 CollectorRegistryCounter counter = Counter.build().name("requests_total").help("Total requests").register();注册后可通过返回的 Counter 实例更新值。
inc()void inc()增加计数器值 1counter.inc();最常用方法,适用于请求数、错误数等递增场景。
inc(double amt)void inc(double amt)增加指定数值counter.inc(2.5);可用于按字节数、处理量等非整数单位累加。
get()double get()获取当前计数值(仅用于测试)double value = counter.get();不建议在生产中频繁读取,主要用于单元测试验证。

3.2 Gauge(仪表)

方法名称语法用途代码示例注意事项
buildGauge.build()创建一个 Gauge 构建器实例Gauge gauge = Gauge.build();用于构造 Gauge 指标。
name.name(String name)设置指标名称.name("memory_usage_bytes")遵循命名规范。
help.help(String help)设置帮助文本.help("Current memory usage in bytes")提供语义说明。
labelNames.labelNames(String... labelNames)定义标签名.labelNames("region")支持多维数据切片。
register.register()注册指标到默认注册表Gauge gauge = Gauge.build().name("cpu_temp_celsius").help("CPU temperature").register();返回 Gauge 实例用于后续操作。
set(double val)void set(double val)设置当前值gauge.set(45.2);适用于温度、内存占用等可上下波动的值。
inc()void inc()值加 1gauge.inc();例如线程数增加。
inc(double amt)void inc(double amt)值增加指定数量gauge.inc(1024);可用于资源增长。
dec()void dec()值减 1gauge.dec();例如任务完成减少待处理数。
dec(double amt)void dec(double amt)值减少指定数量gauge.dec(512);与 inc 对应。
add(double val)void add(double val)inc(double)gauge.add(3.5);别名方法,功能相同。
sub(double val)void sub(double val)dec(double)gauge.sub(2.0);别名方法。

3.3 Histogram(直方图)

方法名称语法用途代码示例注意事项
buildHistogram.build()创建 Histogram 构建器Histogram hist = Histogram.build();用于记录样本分布。
name.name(String name)设置指标名称.name("request_duration_seconds")通常以 _seconds 结尾表示时间单位。
help.help(String help)设置描述信息.help("Request duration in seconds")明确指标用途。
labelNames.labelNames(String... labelNames)定义标签.labelNames("handler")支持按维度划分直方图。
buckets.buckets(double... bounds)自定义桶边界(如 0.1, 0.3, 1.0).buckets(0.1, 0.3, 0.5, 1.0)默认桶为 [.005, .01, .025, ..., 10.0],应根据业务调整。
exponentialBuckets.exponentialBuckets(double start, double factor, int count)创建指数增长的桶.exponentialBuckets(0.1, 2.0, 5)生成 [0.1, 0.2, 0.4, 0.8, 1.6] 共 5 个桶。
linearBuckets.linearBuckets(double start, double width, int count)创建等宽桶.linearBuckets(0, 10, 5)生成 [0,10), [10,20), …, [40,50) 共 5 个桶。
register.register()注册直方图Histogram hist = Histogram.build().name("duration").help("Latency").register();注册后可用于观察样本。
observe(double val)void observe(double val)记录一个样本值hist.observe(0.45);所有桶中满足 le 条件的计数都会 +1,同时 _sum_count 也更新。

3.4 Summary(摘要)

方法名称语法用途代码示例注意事项
buildSummary.build()创建 Summary 构建器Summary summary = Summary.build();用于客户端计算分位数。
name.name(String name)设置指标名称.name("response_size_bytes")命名清晰。
help.help(String help)设置帮助文本.help("Response size distribution")描述用途。
labelNames.labelNames(String... labelNames)定义标签.labelNames("endpoint")支持多维分析。
quantile.quantile(double quantile, double error)定义要计算的分位数及允许误差.quantile(0.95, 0.01)误差越小精度越高但内存消耗越大。
maxAgeSeconds.maxAgeSeconds(int seconds)设置滑动窗口最大持续时间.maxAgeSeconds(600)默认 300 秒(5 分钟)。
ageBuckets.ageBuckets(int count)设置滑动窗口的桶数量.ageBuckets(5)每个桶代表 maxAgeSeconds/ageBuckets 的时间段。
register.register()注册摘要指标Summary summary = Summary.build().name("latency").quantile(0.99, 0.001).register();完成配置并注册。
observe(double val)void observe(double val)记录一个样本值summary.observe(123.4);用于后续分位数计算。

3.5 Untyped(未指定类型)

方法名称语法用途代码示例注意事项
buildUntyped.build()创建 Untyped 构建器Untyped untyped = Untyped.build();表示类型未知的指标。
name.name(String name)设置名称.name("unknown_type_metric")少见使用场景。
help.help(String help)设置描述.help("Metric with unknown type")不推荐主动使用。
labelNames.labelNames(String... labelNames)添加标签支持.labelNames("type")可带维度。
register.register()注册指标Untyped metric = Untyped.build().name("m").help("desc").register();成功注册后可通过 set 设置值。
set(double val)void set(double val)设置当前值metric.set(42.0);类似 Gauge 但无语义含义。
get()double get()获取当前值double v = metric.get();仅测试用途。

第 4 章 自定义指标开发实践

4.1 创建和注册自定义指标

步骤说明注意事项
1. 选择合适的指标类型根据数据特性选择 Counter、Gauge、Histogram 或 Summary错误选择类型会导致查询困难或资源浪费。
2. 定义指标名称使用小写字母、数字和下划线,以 _total_seconds 等后缀增强语义避免使用大写字母和特殊字符。
3. 添加帮助文本使用 .help() 提供清晰描述有助于团队成员理解和使用指标。
4. 设置标签(可选)使用 .labelNames() 定义维度标签不宜过多,避免”高基数”问题。
5. 调用 register() 完成注册将指标注册到 CollectorRegistry注册后自动出现在 /metrics 端点中。
6. 保存引用以更新值将返回的指标实例保存为字段或常量后续通过该引用调用 inc()set() 等方法。

4.2 使用标签(Labels)进行维度划分

方法/概念语法用途代码示例注意事项
labelNames.labelNames("method", "status")在构建时声明标签名Counter counter = Counter.build().name("http_requests_total").labelNames("method", "status").help("HTTP requests by method and status").register();必须提前声明所有标签名。
labels(String… values)counter.labels("GET", "200")获取特定标签组合的 Child 实例counter.labels("GET", "200").inc();参数顺序必须与 labelNames 定义一致。
高基数风险-标签值过多导致时间序列爆炸如使用用户 ID 作为标签会导致内存和查询性能问题,应避免。
推荐标签命名小写字母、下划线分隔统一风格便于查询status_code 而非 statusCode与 Prometheus 社区规范保持一致。
常见标签用途methodstatushandlerregion划分监控维度按 HTTP 方法和状态码统计请求有助于定位问题来源。

4.3 多实例指标管理(Child 和 LabelSet)

概念/方法说明注意事项
Child指标类的内部类,表示带特定标签值的指标实例每个唯一标签组合对应一个 Child。
LabelSet一组标签名到标签值的映射用于唯一标识一个 Child。
自动 Child 创建第一次调用 labels() 时自动创建 Child后续相同标签组合返回同一 Child 实例。
内存消耗每个 Child 占用一定内存高基数标签会导致内存泄漏,需谨慎设计。
预创建 Child可通过 register 方法预定义常用 Child减少运行时开销,提高性能。
线程安全Child 实例本身是线程安全的可在多线程环境中安全调用 inc()set() 等方法。

4.4 线程安全与指标更新最佳实践

实践建议说明注意事项
指标实例线程安全所有标准指标类型(Counter、Gauge 等)的方法都是线程安全的可在多个线程中并发调用 inc()set() 等方法。
避免在循环中创建指标指标应在初始化阶段创建并复用循环中 build().register() 会导致重复注册异常或内存泄漏。
使用静态常量保存指标引用将注册后的指标实例保存为 private static final 字段便于全局访问且避免重复创建。
控制标签基数避免使用高变化性的值作为标签(如请求 ID、用户名)防止时间序列数量爆炸。
合理选择指标类型请求计数用 Counter,延迟用 Histogram,瞬时值用 Gauge类型错用会影响 PromQL 查询效果。
监控指标本身性能过多指标或频繁更新会影响应用性能生产环境应定期审查指标使用情况。
使用 Summary 或 Histogram 记录延迟推荐使用 Histogram,因其更灵活且支持任意分位数计算Summary 在客户端计算分位数,不够精确。

第 5 章 高级特性与性能优化

5.1 Collector 与 Custom Collector 的实现

方法/类语法用途代码示例注意事项
Collectorabstract class CollectorPrometheus 客户端中用于自定义指标收集的基类public class CustomCollector extends Collector { ... }必须重写 collect() 方法。
collect()List collect()返回当前收集到的指标样本列表@Override public List collect() { ... }每次拉取时调用,应尽量轻量、快速完成。
MetricFamilySamplesnew MetricFamilySamples(String name, Type type, String help, List samples)表示一组同名指标(含多个标签变体)List samples = new ArrayList<>(); samples.add(new Sample("my_metric", Arrays.asList("label1"), Arrays.asList("value1"), 123.0)); return Arrays.asList(new MetricFamilySamples("my_metric", Type.GAUGE, "desc", samples));必须指定指标名、类型、帮助文本和样本列表。
Samplenew Sample(String name, List labelNames, List labelValues, double value)表示一个具体的指标样本new Sample("http_requests_total", Arrays.asList("method"), Arrays.asList("GET"), 42.0)labelNameslabelValues 必须一一对应。
register()void register()将自定义 Collector 注册到 CollectorRegistrynew CustomCollector().register();注册后其指标将出现在 /metrics 端点中。
Type 枚举Type.COUNTER, Type.GAUGE, Type.HISTOGRAM, Type.SUMMARY指定指标类型Type.COUNTER必须与实际数据语义匹配。

5.2 使用 Pushgateway 进行推送式上报

方法/类语法用途代码示例注意事项
PushGatewaynew PushGateway(String address)创建指向 Pushgateway 服务的客户端PushGateway pg = new PushGateway("localhost:9091");地址格式为 host:port
pushAdd(CollectorRegistry, String)void pushAdd(CollectorRegistry registry, String job)将指标以”增量”方式推送到指定 jobpg.pushAdd(CollectorRegistry.defaultRegistry, "batch_job_123");相同 job 的多次 pushAdd 会累加指标。
push(CollectorRegistry, String)void push(CollectorRegistry registry, String job)覆盖式推送,替换该 job 下所有指标pg.push(CollectorRegistry.defaultRegistry, "batch_job_123");适用于短期批处理任务状态上报。
push(CollectorRegistry, String, String)void push(CollectorRegistry registry, String job, String instance)指定 job 和 instance 标签推送pg.push(registry, "cron_job", "server1");更细粒度控制目标标识。
delete(String job)void delete(String job)删除指定 job 的所有指标pg.delete("batch_job_123");用于清理过期任务数据。
disableHostnameValidation()pg.disableHostnameValidation()禁用 HTTPS 主机名验证(调试用)pg.disableHostnameValidation();仅用于测试环境,生产不建议使用。
setConnectionFactory()pg.setConnectionFactory(...)设置自定义 HTTP 连接工厂(如支持 HTTPS)-用于复杂网络环境集成。

5.3 指标收集性能调优建议

建议项说明注意事项
减少指标数量只暴露必要指标,避免冗余指标过多会增加序列化和网络传输开销。
控制标签基数避免使用高基数标签(如用户 ID、请求 ID)高基数是性能杀手,会导致内存暴涨和查询变慢。
批量更新替代频繁小更新合并多个 inc() 调用为 inc(n)减少同步开销,提升吞吐。
避免在 hot path 中创建字符串标签值尽量复用,避免临时拼接字符串创建和 GC 会影响应用性能。
使用本地缓存 Child 实例缓存 labels(...) 返回的 Child避免重复查找,提升多标签场景性能。
选择合适采集间隔Prometheus scrape_interval 不宜过短默认 15s 或 30s 足够,过短增加系统压力。
监控指标本身开销关注 /metrics 端点响应时间和大小若响应慢或体积大,需优化指标设计。
使用 Histogram 替代 Summary推荐 Histogram,更灵活且服务端计算分位数Summary 在客户端计算,精度低且难以聚合。

5.4 指标命名规范与最佳实践

规范项说明注意事项
小写字母开头所有指标名应以小写字母开头http_requests_total
使用下划线分隔单词间用下划线 _ 分隔jvm_memory_bytes_used
添加语义后缀Counter 用 _total,Gauge 用 _current_bytes,Histogram 用 _duration_seconds明确单位和类型
避免缩写使用完整单词(如 seconds 而非 sec提高可读性
按层级组织优先级:应用名 > 模块名 > 指标名payment_service_db_connections_used
标签名小写标签名也应小写并用下划线分隔status_code 而非 statusCode
限制标签数量每个指标标签数建议不超过 5-6 个过多标签增加复杂性和开销
文档化指标建立内部指标字典,记录每个指标用途便于新人理解和审计

第 6 章 Spring Boot 集成 Prometheus

6.1 使用 Micrometer 集成 Prometheus

依赖项语法用途代码示例注意事项
micrometer-core<dependency><br>&nbsp;&nbsp;<groupId>io.micrometer</groupId><br>&nbsp;&nbsp;<artifactId>micrometer-core</artifactId><br></dependency>Micrometer 核心库,提供统一的指标抽象自动引入所有监控功能的基础,无需显式声明。
micrometer-registry-prometheus<dependency><br>&nbsp;&nbsp;<groupId>io.micrometer</groupId><br>&nbsp;&nbsp;<artifactId>micrometer-registry-prometheus</artifactId><br></dependency>Prometheus 注册表实现io.micrometer micrometer-registry-prometheus必须引入才能生成 Prometheus 格式指标。
MeterRegistry@Autowired MeterRegistry registry注入 Micrometer 的指标注册表@Autowired private MeterRegistry registry;用于创建和管理自定义指标实例。
@Timed 注解@Timed("http.server.requests")方法级监控,自动记录调用次数和延迟@GetMapping("/api") @Timed("api.duration") public String api() { ... }可用于 Controller 或 Service 方法,支持 percentiles 配置。
自动指标-自动暴露 JVM、HTTP 请求、Tomcat 等运行时指标jvm_memory_used_bytes, http_server_requests_seconds_count开箱即用,极大减少手动编码。

6.2 配置 /actuator/prometheus 端点

配置项语法用途代码示例注意事项
启用 actuator<dependency><br>&nbsp;&nbsp;<groupId>org.springframework.boot</groupId><br>&nbsp;&nbsp;<artifactId>spring-boot-starter-actuator</artifactId><br></dependency>添加 Spring Boot Actuator 支持org.springframework.boot spring-boot-starter-actuator必须引入。
暴露 prometheus 端点management.endpoints.web.exposure.include=prometheusapplication.yml 中配置management: endpoints: web: exposure: include: prometheus默认不暴露,需显式开启。
自定义端点路径management.endpoints.web.path-mapping.prometheus=/metrics修改默认路径management: endpoints: web: path-mapping: prometheus: /metrics便于与 Nginx 等反向代理集成。
安全控制management.endpoint.prometheus.enabled=false控制端点是否启用结合 Spring Security 限制访问 IP 或添加认证生产环境建议启用认证,防止信息泄露。

6.3 自定义指标在 Spring Boot 中的注册

方法语法用途代码示例注意事项
Counter.builder()Counter.builder(String name).register(MeterRegistry)创建并注册计数器Counter.builder("login.attempts").tag("result", "success").register(registry).increment();使用注入的 MeterRegistry。
Gauge.builder()Gauge.builder(String name, T obj, ToDoubleFunction valueFunction)创建 Gauge 指标Gauge.builder("queue.size", queue, Queue::size).register(registry);
Gauge.builder("db.connections.used", dataSource, DataSource::getActiveCount).register(registry);
第二个参数为状态持有对象,函数用于提取值。
Timer.builder()Timer.builder(String name).register(registry)创建 Timer 记录耗时Timer timer = Timer.builder("api.call.duration").register(registry);
timer.record(() -> api.call());
支持 record(Runnable) 简化调用。
DistributionSummary.builder()DistributionSummary.builder(String name).register(registry)类似 Histogram,用于非时间值分布DistributionSummary.builder("payload.size.bytes").register(registry).record(1024);适用于请求大小、消息长度等。
@Bean 定义指标@Bean MeterBinder customMeter() { ... }通过 Bean 方式注册可复用指标@Bean public MeterBinder customCounter() { return registry -> Counter.builder("custom.event").register(registry); }适合全局共享指标,便于测试和复用。

6.4 结合 Grafana 展示 Spring Boot 应用监控

实践项说明注意事项
添加 Prometheus 数据源在 Grafana 中配置 Prometheus 服务地址http://prometheus-server:9090
导入 Spring Boot 仪表盘使用官方 ID 为 6756 的 “Spring Boot Application” 仪表盘可在 Grafana 官网下载并导入
自定义面板使用 PromQL 查询自定义指标rate(http_server_requests_seconds_count{application="myapp"}[5m])
设置告警规则在 Grafana 或 Prometheus 中设置阈值告警如错误率 > 1% 触发告警
多环境区分使用 applicationprofileinstance 等标签区分不同环境查询时添加 {application="prod-app"}
仪表盘共享将常用仪表盘导出为 JSON 并版本化管理便于团队协作和恢复

第 7 章 实战案例与监控设计

7.1 监控 REST API 请求量与延迟

方法/指标说明注意事项
http_server_requests_seconds_countMicrometer 自动提供的 HTTP 请求计数器可通过 methoduristatus 等标签切片分析。
http_server_requests_seconds_sum请求延迟总和用于计算平均延迟:sum / count
rate() 函数rate(http_server_requests_seconds_count[5m])计算每秒请求数(QPS),排除重启影响。
histogram_quantile()histogram_quantile(0.95, sum(rate(http_server_requests_seconds_bucket[5m])))计算 95% 分位延迟,识别慢请求。
@Timed 注解在 Controller 方法上使用自动记录该方法的调用次数和延迟分布。
自定义标签添加业务相关标签如 tenant_id增强多租户场景下的可观测性。

7.2 跟踪方法调用次数与异常率

方法/指标说明注意事项
Counter 记录调用次数counter.increment()在方法入口处调用。
Counter 记录异常次数try { ... } catch (Exception e) { errorCounter.increment(); throw e; }捕获后递增并重新抛出,不影响业务逻辑。
@Timed + extraTags@Timed(extraTags = {"error", "false"}) 和 catch 块中手动标记区分成功与失败调用。
异常率计算rate(error_counter[5m]) / rate(total_counter[5m])Prometheus 中计算错误率。
Gauge 记录当前处理中任务数inc() 进入时,dec() 退出时反映系统负载压力。

7.3 数据库连接池监控

指标名称说明注意事项
hikaricp_connectionsHikariCP 连接总数(Gauge)包括空闲和活跃连接。
hikaricp_connections_active当前活跃连接数(Gauge)高值可能表示慢查询或连接泄漏。
hikaricp_connections_idle空闲连接数(Gauge)与 active 之和等于 total。
hikaricp_connections_pending等待获取连接的线程数(Gauge)>0 表示连接不足,需扩容或优化。
hikaricp_connection_timeout连接获取超时次数(Counter)持续增长需立即排查。
自定义监控定期记录连接使用率:active/total可设置告警阈值(如 >80%)。

7.4 JVM 内存与 GC 指标分析

指标名称说明注意事项
jvm_memory_used_bytes各内存区(heap, non-heap)已使用内存观察老年代使用趋势,判断是否内存泄漏。
jvm_memory_max_bytes各内存区最大容量heap 通常等于 -Xmx 设置值。
jvm_gc_pause_seconds_countGC 停顿次数区分 young gc 和 full gc。
jvm_gc_pause_seconds_sumGC 停顿总时间计算平均停顿时长。
jvm_threads_states_threads各状态线程数量blocked 线程过多可能表示锁竞争。
Full GC 频率告警increase(jvm_gc_pause_seconds_count{action="end of major GC"}[1h]) > 51 小时内发生 5 次以上 Full GC 触发告警。
老年代使用率jvm_memory_used_bytes{area="heap",id="PS Old Gen"} / jvm_memory_max_bytes{area="heap",id="PS Old Gen"}持续接近 100% 需优化或扩容。

第 8 章 安全与生产部署

8.1 指标端点的安全保护(认证与授权)

方法说明注意事项
Spring Security 集成配置安全规则保护 /actuator/prometheushttp.authorizeRequests().requestMatchers("/actuator/prometheus").hasRole("MONITOR");
网络层防护使用防火墙或 Ingress 限制访问 IP仅允许 Prometheus Server IP 访问。
Basic Auth在反向代理(如 Nginx)上配置用户名密码location /actuator/prometheus { auth_basic "Prometheus"; auth_basic_user_file /etc/nginx/.htpasswd; }
JWT/OAuth2复杂场景下使用令牌认证需定制化开发。
禁用敏感端点production 环境关闭 envbeans 等敏感 actuator 端点management.endpoints.web.exposure.include=health,info,prometheus

8.2 大规模应用中的指标聚合策略

策略说明注意事项
分层采集边缘服务 -> Service Mesh (Istio) -> Prometheus Federation减轻中心 Server 压力。
Prometheus Federation上层 Prometheus 只拉取下层聚合结果global: external_labels: level: region
Thanos 或 Cortex使用支持水平扩展的长期存储方案提供全局查询视图。
指标采样只采集关键业务指标,忽略低价值数据如关闭某些 trace-level 指标。
降频采集非核心服务使用更长的 scrape_interval如从 15s 调整为 60s。

8.3 高并发场景下的指标采集稳定性

实践说明注意事项
指标更新异步化使用队列缓冲指标更新,异步刷入避免阻塞业务线程。
减少 synchronized 块避免在指标更新路径上加锁使用无锁数据结构(如 LongAdder)。
监控 /metrics 响应时间记录端点自身性能若 >1s 需优化。
限制指标数量避免创建过多时间序列检查 CollectorRegistry.defaultRegistry.metricFamilySamples().size()
使用 Histogram 替代 SummarySummary 在客户端计算分位数开销大Histogram 仅记录原始值。
资源隔离将监控组件部署在独立线程池防止相互影响。

8.4 Prometheus 配置与 Java 应用的协同部署

实践说明注意事项
统一 scrape_intervalJava 应用指标更新频率与 Prometheus 拉取间隔匹配如均为 30s。
正确设置 job 和 instance 标签确保 Prometheus 配置中的 target 正确标识应用static_configs: - targets: ['app1:8080'] labels: job: payment-service
启用 relabeling动态重写标签,便于分类和路由使用 replacementregex
配置 scrape_timeout设置合理的超时时间scrape_timeout: 10s
健康检查集成/actuator/health 与 Prometheus 告警联动job up 状态异常触发告警。
版本对齐保持 prometheus-client 与 Spring Boot / Micrometer 版本兼容查阅官方兼容性矩阵。