Micrometer + Prometheus 监控体系
1. 概述
Micrometer 是 Spring Boot 官方推荐的指标收集门面(Facade),类似于 SLF4J 在日志领域的地位。它为 JVM 平台应用提供通用的度量工具 API,支持将数据导出到多种监控系统(Prometheus、Datadog、New Relic、Graphite 等)。Prometheus 是一款开源的系统监控与告警工具包,通过拉取(Pull)方式采集指标数据。Grafana 提供可视化仪表盘。
本文将从基础概念入手,逐步深入到源码分析和实战案例。
2. 核心依赖与自动配置
2.1 Maven 依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-registry-prometheus</artifactId>
</dependency>spring-boot-starter-actuator 包含 micrometer-core。micrometer-registry-prometheus 提供 Prometheus 注册表实现,将指标序列化为 Prometheus 文本格式。
2.2 application.yml 配置
management:
endpoints:
web:
exposure:
include: health,metrics,prometheus
metrics:
tags:
application: ${spring.application.name:unknown}
export:
prometheus:
enabled: true
step: 30s
server:
port: 8081关键说明:
management.endpoints.web.exposure.include— 暴露 Actuator 端点management.metrics.tags— 全局通用标签(如应用名),所有指标都会携带management.metrics.export.prometheus.step— 指标导出频率
启动后访问 http://localhost:8081/actuator/prometheus 即可看到原始指标数据。
2.3 自动配置原理
PrometheusMetricsExportAutoConfiguration 在检测到类路径存在 PrometheusMeterRegistry 时自动装配:
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(PrometheusMeterRegistry.class)
@ConditionalOnProperty(prefix = "management.metrics.export.prometheus", name = "enabled", matchIfMissing = true)
public class PrometheusMetricsExportAutoConfiguration {
@Bean
@ConditionalOnMissingBean
public PrometheusMeterRegistry prometheusMeterRegistry(
MeterRegistry meterRegistry, PrometheusProperties config) {
// ...
}
}MeterRegistry 是 Micrometer 的核心抽象层,所有指标注册都通过它完成。
3. MeterRegistry
3.1 定义与作用
MeterRegistry 是整个 Micrometer 指标体系的注册中心。它负责创建、管理和聚合各类 Meter(度量器)。每个 Meter 都有一个唯一名称和一组标签(Tag)。
3.2 CompositeMeterRegistry
Micrometer 支持组合模式,允许同时向多个监控系统推送指标:
CompositeMeterRegistry composite = new CompositeMeterRegistry();
composite.add(new SimpleMeterRegistry());
composite.add(new PrometheusMeterRegistry(PrometheusConfig.DEFAULT));Spring Boot 自动配置的 MeterRegistry 实例已经是一个 CompositeMeterRegistry,它会将数据分发给所有已注册的 MeterRegistry 实现(包括 PrometheusMeterRegistry)。
3.3 MeterRegistry 的常用方法
@Service
public class MetricsService {
private final MeterRegistry registry;
public MetricsService(MeterRegistry registry) {
this.registry = registry;
}
public void demonstrate() {
// Counter
registry.counter("my.counter", "tag1", "value1").increment();
// Gauge
registry.gauge("my.gauge", Tags.of("tag1", "value1"), 42.0);
// Timer
registry.timer("my.timer").record(() -> doSomething());
// DistributionSummary
registry.summary("my.summary").record(100);
}
private void doSomething() {
// ...
}
}4. Counter(计数器)
4.1 概念
Counter 表示一个单调递增的计数器,适用于记录请求总数、错误总数、任务完成数等。其值只能增加,不能减少(系统重启时重置)。
4.2 创建与使用
@Component
public class OrderCounter {
private final Counter orderCreatedCounter;
public OrderCounter(MeterRegistry registry) {
orderCreatedCounter = Counter.builder("order.created.total")
.description("累计订单创建数量")
.tag("region", "cn-east")
.register(registry);
}
public void increment() {
orderCreatedCounter.increment();
}
public void increment(double amount) {
orderCreatedCounter.increment(amount);
}
}4.3 方法签名说明
| 方法 | 说明 |
|---|---|
increment() | 自增 1 |
increment(double amount) | 增加指定数量 |
count() | 获取当前计数值(double) |
4.4 底层实现要点
Counter 接口的核心方法 increment(double amount) 要求 amount 必须为正数。在 PrometheusMeterRegistry 中,Counter 被映射为 Prometheus 的 Counter 类型(带 _total 后缀),支持 rate() 函数查询每秒增长率。
5. Gauge(仪表)
5.1 概念
Gauge 表示一个可上下波动的瞬时值,适用于当前线程数、内存使用量、队列长度等场景。与 Counter 不同,Gauge 的值可以增加也可以减少。
5.2 创建方式
方式一:从对象引用获取
@Component
public class QueueGauge {
private final Queue<String> queue = new ConcurrentLinkedQueue<>();
public QueueGauge(MeterRegistry registry) {
registry.gauge("queue.size", Tags.empty(), queue, Queue::size);
}
}方式二:通过 lambda 表达式
AtomicInteger concurrentOrders = registry.gauge("order.concurrent.processing",
Tags.of("status", "processing"),
new AtomicInteger(0),
AtomicInteger::get);方式三:使用 Gauge.builder
Gauge.builder("jvm.memory.used", memoryPool,
pool -> pool.getUsage().getUsed())
.description("JVM 内存使用量")
.tag("pool", "metaspace")
.register(registry);5.3 重要注意事项
Gauge 是弱引用的——当被包装的对象被 GC 回收后,该 Gauge 也会自动注销。这就解释了为什么推荐使用 AtomicInteger、AtomicLong 等不受 GC 影响的对象。
6. Timer(计时器)
6.1 概念
Timer 用于测量耗时和调用频率。它会同时记录:
- 总调用次数(count)
- 总耗时(totalTime)
- 最大耗时(max)
Prometheus 导出时会生成 _seconds_count、_seconds_sum、_seconds_max 三个指标。
6.2 使用方式
@Component
public class PaymentTimer {
private final Timer paymentTimer;
public PaymentTimer(MeterRegistry registry) {
paymentTimer = Timer.builder("payment.process.time")
.description("支付处理耗时")
.tag("payment_method", "wechat")
.publishPercentiles(0.5, 0.95, 0.99)
.publishPercentileHistogram()
.register(registry);
}
// 方式一:Runnable/Callable
public void processPaymentWithTimer() {
paymentTimer.record(() -> processPayment());
}
// 方式二:Supplier<T>
public PaymentResult processPaymentWithResult() {
return paymentTimer.record(() -> doProcessPayment());
}
// 方式三:手动记录
public void processPaymentManual() {
Timer.Sample sample = Timer.start(registry);
try {
processPayment();
} finally {
sample.stop(paymentTimer);
}
}
// 方式四:记录 Duration
public void processPaymentDuration() {
long start = System.nanoTime();
processPayment();
long duration = System.nanoTime() - start;
paymentTimer.record(duration, TimeUnit.NANOSECONDS);
}
private void processPayment() {
// 模拟支付处理
}
private PaymentResult doProcessPayment() {
return new PaymentResult(true);
}
}6.3 百分位数与直方图
Timer timer = Timer.builder("api.response.time")
.publishPercentiles(0.5, 0.95, 0.99) // 客户端百分位估算
.publishPercentileHistogram() // 生成 Prometheus 直方图
.sla(Duration.ofMillis(100), Duration.ofMillis(500)) // 预设边界值
.register(registry);publishPercentiles— 在客户端计算百分位值(对 Prometheus 而言,更推荐使用服务端的histogram_quantile)publishPercentileHistogram— 生成_bucket直方图,允许 Prometheus 服务端计算histogram_quantile
7. DistributionSummary(分布摘要)
7.1 概念
DistributionSummary 与 Timer 类似,但它跟踪的是任意分布值而非耗时。适用于消息大小、HTTP 响应体大小、订单金额等场景。
7.2 使用示例
@Component
public class OrderAmountSummary {
private final DistributionSummary orderAmountSummary;
public OrderAmountSummary(MeterRegistry registry) {
orderAmountSummary = DistributionSummary.builder("order.amount")
.description("订单金额分布")
.baseUnit("yuan")
.tags("currency", "CNY")
.publishPercentiles(0.5, 0.95, 0.99)
.publishPercentileHistogram()
.minimumExpectedValue(1.0)
.maximumExpectedValue(10000.0)
.register(registry);
}
public void recordOrderAmount(double amount) {
orderAmountSummary.record(amount);
}
}7.3 Timer 与 DistributionSummary 对比
| 维度 | Timer | DistributionSummary |
|---|---|---|
| 测量对象 | 耗时 | 任意数值分布 |
| 时间单位 | 自动处理 | 无(需指定 baseUnit) |
| 默认 SNAPPY 经验分位数 | 是 | 是 |
| 典型场景 | API 响应时间、DB 查询耗时 | 请求体大小、订单金额 |
8. @Timed 注解
8.1 基本用法
Spring Boot 支持通过 @Timed 注解自动为方法或类生成 Timer 指标:
@RestController
@RequestMapping("/api/orders")
@Timed // 类级别:该 Controller 下所有方法都会被计时
public class OrderController {
@PostMapping
@Timed(value = "order.create.time",
extraTags = {"version", "v1"},
percentiles = {0.5, 0.95, 0.99},
histogram = true)
public Order createOrder(@RequestBody CreateOrderRequest request) {
// ...
}
@GetMapping("/{id}")
public Order getOrder(@PathVariable String id) {
// ...
}
}8.2 启用 @Timed 支持
@Configuration
@EnableAspectJAutoProxy
public class MetricsConfig {
@Bean
public TimedAspect timedAspect(MeterRegistry registry) {
return new TimedAspect(registry);
}
}8.3 注解参数详解
| 参数 | 默认值 | 说明 |
|---|---|---|
value | "" | 指标名称前缀 |
longTask | false | 是否使用 LongTaskTimer |
extraTags | {} | 额外标签 |
percentiles | {} | 需要发布的百分位 |
histogram | false | 是否生成直方图 |
description | "" | 指标描述 |
9. 自定义指标
9.1 自定义 Tag 标签
标签是 Micrometer 实现多维分析的核心手段。标签定义应遵循基数控制原则——避免使用用户 ID、订单 ID 等无限增长的标签值。
@Configuration
public class CommonTagsConfig {
@Bean
public MeterRegistryCustomizer<MeterRegistry> commonTags() {
return registry -> registry.config().commonTags(
"application", "order-service",
"environment", "production",
"cluster", "cluster-a"
);
}
}9.2 完整自定义指标组件
@Component
public class BusinessMetrics {
private final Counter orderCreateCounter;
private final Timer orderCreateTimer;
private final DistributionSummary orderAmountSummary;
private final AtomicInteger concurrentOrders;
public BusinessMetrics(MeterRegistry registry) {
// 订单创建计数器
this.orderCreateCounter = registry.counter("business.order.created",
"type", "total");
// 订单创建耗时
this.orderCreateTimer = Timer.builder("business.order.create.time")
.description("订单创建响应时间")
.publishPercentiles(0.5, 0.95, 0.99)
.publishPercentileHistogram()
.register(registry);
// 订单金额分布
this.orderAmountSummary = DistributionSummary.builder("business.order.amount")
.description("订单金额分布")
.baseUnit("yuan")
.publishPercentiles(0.5, 0.95, 0.99)
.minimumExpectedValue(0.01)
.maximumExpectedValue(999999.0)
.register(registry);
// 并发处理数
this.concurrentOrders = registry.gauge("business.order.concurrent",
new AtomicInteger(0));
}
public void recordOrderCreated(long durationMs, double amount) {
orderCreateCounter.increment();
orderCreateTimer.record(durationMs, TimeUnit.MILLISECONDS);
orderAmountSummary.record(amount);
}
public void incrementConcurrent() {
concurrentOrders.incrementAndGet();
}
public void decrementConcurrent() {
concurrentOrders.decrementAndGet();
}
}9.3 通过低级 API 构建 Meter
@Bean
public Counter customCounter(MeterRegistry registry) {
return Meter.builder("custom.meter", Meter.Type.COUNTER,
List.of(new Measurement(() -> 1.0, Statistic.COUNT)))
.description("自定义底层实现")
.tag("source", "low-level-api")
.register(registry);
}10. Prometheus + Grafana 整合
10.1 Prometheus 配置
创建 prometheus.yml:
global:
scrape_interval: 15s
evaluation_interval: 15s
scrape_configs:
- job_name: 'spring-boot-app'
metrics_path: '/actuator/prometheus'
static_configs:
- targets: ['localhost:8081']
labels:
application: 'order-service'启动 Prometheus:
prometheus --config.file=prometheus.yml10.2 验证 Prometheus 数据采集
访问 http://localhost:9090/targets 检查 Target 状态。在查询框中输入 rate(http_server_requests_seconds_count[1m]) 查看 QPS。
10.3 Grafana 仪表盘配置
添加数据源:Configuration → Data Sources → Add → Prometheus,URL 填写
http://localhost:9090导入仪表盘:可以使用社区模板(Dashboards → Import → 输入 ID
4701为 JVM 仪表盘)。也可以自行创建 Panel。
10.4 常用 PromQL 查询
# 每秒请求数 (QPS)
rate(http_server_requests_seconds_count[1m])
# P99 响应时间 (毫秒)
histogram_quantile(0.99,
rate(http_server_requests_seconds_bucket[5m])
) * 1000
# 错误率
sum(rate(http_server_requests_seconds_count{status=~"5.."}[1m]))
/
sum(rate(http_server_requests_seconds_count[1m]))
# JVM 堆内存使用率
jvm_memory_used_bytes{area="heap"} / jvm_memory_max_bytes{area="heap"}11. Micrometer 指标体系源码分析
11.1 核心类结构
Meter (接口)
├── Counter
├── Gauge
├── Timer
├── DistributionSummary
├── LongTaskTimer
└── FunctionCounter / FunctionTimer
MeterRegistry (抽象类)
├── SimpleMeterRegistry (内存存储,调试用)
├── CompositeMeterRegistry (组合模式,支持多注册表)
└── PrometheusMeterRegistry (Prometheus 实现)11.2 MeterRegistry 注册流程
register() 方法的调用链:
MeterRegistry.register(Meter meter)
→ MeterRegistry.registerMeterIfNecessary(Meter meter)
→ MeterRegistry.onPut(Meter meter) // 子类实现
→ MeterRegistry.applyToEachMeterRegistry // Composite 模式
→ PrometheusMeterRegistry.onPut()
→ PrometheusCollectorRegistry 的 add() 或 addOrGet()核心源码片段(SimpleMeterRegistry 的简化实现):
// SimpleMeterRegistry 内部维护一个 ConcurrentHashMap
private final ConcurrentHashMap<Id, Meter> meterMap = new ConcurrentHashMap<>();
@Override
protected <M extends Meter> M registerMeterIfNecessary(Meter meter) {
Id id = meter.getId();
Meter existing = meterMap.putIfAbsent(id, meter);
if (existing == null) {
return meter; // 首次注册
}
// 已存在,检查类型是否一致
if (existing.getClass().equals(meter.getClass())) {
return (M) existing; // 返回已有实例
}
throw new IllegalArgumentException(
"Cannot register '" + id + "' as " + meter.getClass().getSimpleName()
+ " because it is already registered as " + existing.getClass().getSimpleName()
);
}11.3 Counter 接口分析
public interface Counter extends Meter {
// 单调递增
void increment(double amount);
default void increment() {
increment(1.0);
}
double count();
}PrometheusMeterRegistry 中的 Counter 实现本质是对 SimpleCollector 的封装,最终通过 CollectorRegistry 输出为 Prometheus 兼容格式。
11.4 Timer 的 Sample 实现分析
public class Timer {
public static class Sample {
private final long startTime;
private final Clock clock;
Sample(Clock clock) {
this.clock = clock;
this.startTime = clock.monotonicTime();
}
public long stop(Timer timer) {
// 计算经过时间 (纳秒)
long elapsed = clock.monotonicTime() - startTime;
timer.record(elapsed, TimeUnit.NANOSECONDS);
return elapsed;
}
}
}11.5 PrometheusMeterRegistry 的序列化
当访问 /actuator/prometheus 时,调用链:
PrometheusScrapeEndpoint.prometheus()
→ PrometheusMeterRegistry.scrape()
→ PrometheusCollectorRegistry.metricFamilySamples()
→ TextFormat.write004(writer, registry.metricFamilySamples())scrape() 方法输出纯文本格式,格式示例如下:
# HELP http_server_requests_seconds_max
# TYPE http_server_requests_seconds_max gauge
http_server_requests_seconds_max{method="GET",status="200",uri="/api/orders",} 0.123
# HELP http_server_requests_seconds
# TYPE http_server_requests_seconds histogram
http_server_requests_seconds_bucket{method="GET",status="200",uri="/api/orders",le="0.001",} 0
http_server_requests_seconds_bucket{method="GET",status="200",uri="/api/orders",le="0.01",} 5
http_server_requests_seconds_bucket{method="GET",status="200",uri="/api/orders",le="0.1",} 120
http_server_requests_seconds_bucket{method="GET",status="200",uri="/api/orders",le="+Inf",} 150
http_server_requests_seconds_count{method="GET",status="200",uri="/api/orders",} 150
http_server_requests_seconds_sum{method="GET",status="200",uri="/api/orders",} 12.512. 实战:电商核心业务指标埋点
12.1 场景说明
以一个典型的电商订单服务为例,需要监控以下核心指标:
| 指标 | 类型 | 说明 |
|---|---|---|
| 订单创建量 | Counter | 累计订单创建数 |
| 支付成功率 | Counter / ratio | 成功支付 / 总支付尝试 |
| P99 响应时间 | Timer | 订单创建接口 P99 |
| 库存扣减并发数 | Gauge | 当前正在扣减库存的线程数 |
12.2 完整代码实现
@Component
public class ECommerceMetrics {
// ====== 订单指标 ======
private final Counter orderCreatedTotal;
private final Counter paymentSuccessTotal;
private final Counter paymentFailTotal;
private final Timer orderCreateTimer;
private final Timer paymentProcessTimer;
// ====== 库存指标 ======
private final AtomicInteger stockDeductionConcurrent;
private final Counter stockDeductionSuccess;
private final Counter stockDeductionFail;
private final Timer stockDeductionTimer;
public ECommerceMetrics(MeterRegistry registry) {
// 订单创建量
this.orderCreatedTotal = Counter.builder("ecommerce.order.created.total")
.description("累计订单创建数量")
.tag("source", "api")
.register(registry);
// 支付成功/失败(计算支付成功率 = success / (success + fail))
this.paymentSuccessTotal = Counter.builder("ecommerce.payment.total")
.description("支付成功次数")
.tag("result", "success")
.register(registry);
this.paymentFailTotal = Counter.builder("ecommerce.payment.total")
.description("支付失败次数")
.tag("result", "fail")
.register(registry);
// 订单创建 P99 响应时间
this.orderCreateTimer = Timer.builder("ecommerce.order.create.time")
.description("订单创建接口响应时间")
.publishPercentileHistogram()
.sla(
Duration.ofMillis(50),
Duration.ofMillis(100),
Duration.ofMillis(200),
Duration.ofMillis(500),
Duration.ofSeconds(1)
)
.register(registry);
// 支付处理耗时
this.paymentProcessTimer = Timer.builder("ecommerce.payment.process.time")
.description("支付处理耗时")
.publishPercentiles(0.5, 0.95, 0.99)
.register(registry);
// 库存扣减并发数
this.stockDeductionConcurrent = registry.gauge(
"ecommerce.stock.deduction.concurrent",
new AtomicInteger(0));
// 库存扣减成功/失败
this.stockDeductionSuccess = Counter.builder("ecommerce.stock.deduction.total")
.tag("result", "success")
.register(registry);
this.stockDeductionFail = Counter.builder("ecommerce.stock.deduction.total")
.tag("result", "fail")
.register(registry);
// 库存扣减耗时
this.stockDeductionTimer = Timer.builder("ecommerce.stock.deduction.time")
.description("库存扣减耗时")
.publishPercentileHistogram()
.register(registry);
}
/**
* 记录订单创建
*/
public void recordOrderCreated(Runnable orderLogic) {
orderCreatedTotal.increment();
orderCreateTimer.record(orderLogic);
}
/**
* 记录支付结果
*/
public void recordPaymentResult(boolean success, Runnable paymentLogic) {
paymentProcessTimer.record(paymentLogic);
if (success) {
paymentSuccessTotal.increment();
} else {
paymentFailTotal.increment();
}
}
/**
* 带并发数跟踪的库存扣减
*/
public <T> T recordStockDeduction(Callable<T> deductionLogic) throws Exception {
stockDeductionConcurrent.incrementAndGet();
try {
Timer.Sample sample = Timer.start();
T result = deductionLogic.call();
sample.stop(stockDeductionTimer);
stockDeductionSuccess.increment();
return result;
} catch (Exception e) {
stockDeductionFail.increment();
throw e;
} finally {
stockDeductionConcurrent.decrementAndGet();
}
}
/**
* 获取支付成功率(供 Prometheus / Grafana 使用)
*/
public double getPaymentSuccessRate() {
double success = paymentSuccessTotal.count();
double total = success + paymentFailTotal.count();
return total == 0 ? 1.0 : success / total;
}
}12.3 业务层集成
@Service
public class OrderService {
private final ECommerceMetrics metrics;
public OrderService(ECommerceMetrics metrics) {
this.metrics = metrics;
}
public Order createOrder(CreateOrderRequest request) {
Order order = new Order();
metrics.recordOrderCreated(() -> {
// 实际的订单创建业务逻辑
// 校验参数 -> 扣减库存 -> 生成订单 -> 发送消息
// ...
});
return order;
}
public PaymentResult processPayment(Long orderId, PaymentRequest request) {
boolean success = false;
try {
// 实际的支付处理逻辑
// ...
success = true;
return new PaymentResult(true, "支付成功");
} finally {
metrics.recordPaymentResult(success, () -> {
// 支付处理耗时在此记录
});
}
}
public void deductStock(Long skuId, int quantity) throws Exception {
metrics.recordStockDeduction(() -> {
// 实际的库存扣减逻辑
// ...
return null;
});
}
}12.4 在 Controller 层暴露支付成功率端点
@RestController
@RequestMapping("/internal/metrics")
public class InternalMetricsController {
private final ECommerceMetrics metrics;
public InternalMetricsController(ECommerceMetrics metrics) {
this.metrics = metrics;
}
@GetMapping("/payment-success-rate")
public ResponseEntity<Map<String, Double>> paymentSuccessRate() {
return ResponseEntity.ok(Map.of(
"paymentSuccessRate", metrics.getPaymentSuccessRate()
));
}
}12.5 对应的 Grafana PromQL
# 订单创建 QPS
rate(ecommerce_order_created_total[1m])
# 支付成功率
sum(rate(ecommerce_payment_total{result="success"}[5m]))
/
sum(rate(ecommerce_payment_total[5m]))
# 订单创建 P99 (毫秒)
histogram_quantile(0.99,
rate(ecommerce_order_create_time_seconds_bucket[5m])
) * 1000
# 库存扣减并发数
ecommerce_stock_deduction_concurrent12.6 Prometheus 告警规则
groups:
- name: ecommerce-alerts
rules:
- alert: HighOrderLatency
expr: histogram_quantile(0.99, rate(ecommerce_order_create_time_seconds_bucket[5m])) > 1
for: 5m
labels:
severity: warning
annotations:
summary: "订单创建 P99 超过 1 秒"
- alert: LowPaymentSuccessRate
expr: sum(rate(ecommerce_payment_total{result="success"}[5m])) / sum(rate(ecommerce_payment_total[5m])) < 0.95
for: 3m
labels:
severity: critical
annotations:
summary: "支付成功率低于 95%"
- alert: HighStockDeductionConcurrency
expr: ecommerce_stock_deduction_concurrent > 50
for: 1m
labels:
severity: warning
annotations:
summary: "库存扣减并发超过 50"
- alert: ZeroOrderCreated
expr: rate(ecommerce_order_created_total[5m]) == 0
for: 10m
labels:
severity: critical
annotations:
summary: "连续 10 分钟无订单创建"13. 最佳实践与注意事项
13.1 标签基数控制
标签值基数过高会导致 Prometheus 内存膨胀和性能下降。永远不要将以下值作为标签:
- ❌ 用户 ID、订单 ID、Session ID
- ❌ Email 地址、手机号
- ❌ IP 地址(有限场景下可用)
✅ 推荐的标签:
- 请求方法(GET/POST)
- HTTP 状态码分类(2xx/4xx/5xx)
- 服务名、集群名
- 错误类型枚举
13.2 指标命名规范
Prometheus 官方推荐命名格式:
<namespace>_<subsystem>_<name>_<unit>[_suffix]示例:
ecommerce_order_created_total— 电商订单累计创建数ecommerce_payment_process_seconds_max— 支付处理最大耗时
13.3 常见陷阱
Gauge 的弱引用问题:如果 Gauge 引用的对象被 GC 掉,指标会消失。使用
AtomicInteger等持有强引用的对象包装 Gauge。Counter 的 double 精度:
counter.count()返回double,当值极大时可能有精度损失。大流量场景建议使用FunctionCounter。Timer 总量溢出:
totalTime使用double纳秒累计,极高频调用下可能溢出。PrometheusMeterRegistry内部使用DoubleAdder缓解此问题。@Timed 的 AOP 性能开销:对于极高吞吐方法,优先使用手动埋点以减少反射开销。
13.4 完整依赖版本参考
<properties>
<java.version>17</java.version>
<spring-boot.version>3.2.0</spring-boot.version>
<micrometer.version>1.12.0</micrometer.version>
</properties>Spring Boot 3.x 默认使用 Micrometer 1.12+,与 Prometheus 整合时确保 micrometer-registry-prometheus 版本与 micrometer-core 一致。
14. 总结
本文从 Micrometer 的五大核心度量器(Counter、Gauge、Timer、DistributionSummary、@Timed)出发,详细介绍了各自的适用场景和 API 用法。在此基础上,通过 Prometheus 和 Grafana 的整合实现了指标的采集与可视化。最后,通过一个完整的电商业务指标埋点实战案例,展示了如何将理论知识应用到真实项目中。
Micrometer 作为 Spring Boot Actuator 的默认指标门面,大幅降低了应用监控的接入成本。配合 Prometheus 的 Pull 模型和 Grafana 的丰富可视化能力,可以快速构建一套生产级的应用监控体系。建议开发者在项目初期就引入指标系统,随着业务演进逐步补充和完善监控维度。