Micrometer 常见注解详解
Micrometer 提供了一组强大的注解(Annotations),用于通过 AOP(面向切面编程)的方式,非侵入式地自动采集方法级别的监控指标,如调用次数、执行时间、异常统计等。
这些注解极大地简化了自定义指标的开发,无需在业务代码中手动调用 MeterRegistry,即可实现对关键方法的性能监控。
一、核心注解概览
| 注解 | 作用 |
|---|---|
@Counted | 统计方法调用次数(Counter) |
@Timed | 记录方法执行时间(Timer) |
@Metered | (已弃用)旧版 Dropwizard 注解,不推荐使用 |
⚠️ 注意:Micrometer 的注解需配合 AOP 支持(如 Spring AOP 或 AspectJ)才能生效。
二、@Counted 注解详解
1. 作用
- 自动为方法创建一个
Counter,每次方法成功或失败时递增。 - 可区分成功与异常调用。
2. 属性说明
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
value | String | "counter" | 指标名称(name) |
description | String | "" | 指标描述 |
countExceptions | boolean | false | 是否对异常也计数 |
recordFailuresOnly | boolean | false | 是否只记录异常调用(失败次数) |
extraTags | String[] | {} | 额外标签(key-value 数组) |
3. 使用示例
@Service
public class OrderService {
// 记录方法调用总次数
@Counted(value = "orders.created", description = "订单创建总次数")
public void createOrder() {
// 业务逻辑
}
// 只记录异常调用次数
@Counted(value = "orders.failed", recordFailuresOnly = true)
public void processPayment() {
if (Math.random() < 0.1) {
throw new PaymentException("支付失败");
}
}
// 对所有异常也计数(包括 RuntimeException)
@Counted(value = "api.calls", countExceptions = true,
extraTags = {"method", "checkout"})
public String checkout() {
// ...
}
}
4. 生成的指标示例(Prometheus 格式)
orders_created_total{exception="none"} 42.0
orders_failed_total{exception="PaymentException"} 3.0
api_calls_total{exception="IllegalArgumentException",method="checkout"} 1.0
✅
exception标签自动记录异常类型,none表示无异常。
三、@Timed 注解详解
1. 作用
- 自动为方法创建一个
Timer,记录方法的执行时间。 - 支持记录分位数(percentiles)、直方图(histogram)等。
2. 属性说明
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
value | String | "timer" | 指标名称 |
description | String | "" | 描述 |
percentiles | double[] | {} | 要发布的分位数(如 0.5, 0.95, 0.99) |
publishPercentileHistogram | boolean | false | 是否发布百分位直方图(Prometheus 推荐) |
extraTags | String[] | {} | 额外标签 |
longTask | boolean | false | 是否使用 LongTaskTimer(适用于长时间任务) |
3. 使用示例
@Service
public class UserService {
// 记录用户查询耗时
@Timed(value = "user.service.find",
percentiles = {0.5, 0.95, 0.99},
publishPercentileHistogram = true,
extraTags = {"category", "read"})
public User findById(String id) {
// 模拟延迟
try { Thread.sleep(100); } catch (InterruptedException e) {}
return new User(id);
}
// 监控长时间运行的任务
@Timed(value = "batch.job.process", longTask = true)
public void runBatchJob() {
// 批处理任务,可能运行几分钟
Thread.sleep(60_000);
}
}
4. 生成的指标示例(Prometheus)
# 普通 Timer
user_service_find_seconds_count{category="read"} 100.0
user_service_find_seconds_sum{category="read"} 12.345
user_service_find_seconds_max{category="read"} 0.25
# 分位数(需配置 percentiles)
user_service_find_seconds{quantile="0.5",category="read"} 0.12
user_service_find_seconds{quantile="0.95",category="read"} 0.22
# LongTaskTimer
batch_job_process_seconds_active 1.0
batch_job_process_seconds_duration{...} 60.0
四、注解的底层实现原理
Micrometer 通过 AOP 切面(Aspect)拦截带有注解的方法:
1. CountedAspect
- 拦截
@Counted方法 - 方法执行后调用
counter.increment() - 根据是否抛出异常设置
exception标签
2. TimedAspect
- 方法开始前记录时间
- 执行完成后调用
timer.record(duration) - 支持
longTask模式(使用LongTaskTimer.Sample)
3. 自动创建 Meter
- 第一次调用时,通过
MeterRegistry创建对应的Counter或Timer - 后续调用复用已创建的 Meter
五、启用 Micrometer 注解支持
1. 添加依赖(Maven)
<!-- Spring AOP -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</aspectjweaver>
</dependency>
<!-- Micrometer 注解支持(包含切面) -->
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-core</artifactId>
</dependency>
✅
micrometer-core已包含CountedAspect和TimedAspect
2. 启用 AspectJ 自动代理(Spring Boot 默认已启用)
@Configuration
@EnableAspectJAutoProxy // 通常不需要显式添加
public class MetricsConfig {
}
Spring Boot 会自动配置 AOP 支持。
六、最佳实践与注意事项
✅ 推荐做法
-
用于关键业务方法
@Timed("order.service.create") public Order createOrder() { ... } -
添加有意义的标签
@Timed(value = "db.query", extraTags = {"table", "users"}) -
启用直方图以支持 Prometheus
@Timed(value = "api.call", publishPercentileHistogram = true) -
避免在高频调用方法上使用(性能考虑)
- 如 getter/setter、循环内方法
-
结合通用标签使用
management: metrics: tags: application: ${spring.application.name}
❌ 避免的问题
-
重复注解导致重复计数
@Timed("service.method") @Timed("another.timer") // ❌ 不支持多个 @Timed -
在 private 方法上使用(AOP 无法拦截)
@Timed("private.method") private void helper() { } // ❌ 无效 -
未启用 AOP
- 忘记引入
spring-aop或aspectjweaver
- 忘记引入
七、与 Spring Boot Actuator 的集成
Spring Boot 自动配置了:
MeterRegistryBean- AOP 基础设施
@Counted和@Timed的切面(只要类路径存在)
你只需:
- 添加依赖
- 使用注解
- 访问
/actuator/metrics查看指标
八、常见问题解答
❓ @Timed 和 @Counted 可以同时使用吗?
✅ 可以,会分别创建 Timer 和 Counter。
@Timed("service.process")
@Counted("service.process.invocations")
public void process() { }
❓ 如何自定义异常标签?
默认使用异常类名(如 IllegalArgumentException)。
如需自定义,可通过 MeterFilter 重写:
@Bean
public MeterFilter renameExceptionTag() {
return MeterFilter.map(id -> {
String exception = id.getTag("exception");
if ("IllegalArgumentException".equals(exception)) {
return id.replaceTag("exception", "validation.error");
}
return id;
});
}
❓ @Timed 的 percentiles 和 histogram 如何选择?
| 方式 | 优点 | 缺点 | 推荐场景 |
|---|---|---|---|
percentiles | 精确 | 内存占用高,不适合高吞吐 | 低频关键接口 |
publishPercentileHistogram | 低内存,Prometheus 友好 | 近似值 | 高频接口、生产环境 |
✅ 生产推荐:启用
publishPercentileHistogram = true
九、总结
| 注解 | 类型 | 适用场景 |
|---|---|---|
@Counted | Counter | 调用次数、失败次数统计 |
@Timed | Timer | 方法耗时、响应时间监控 |
使用建议:
- ✅ 优先用于 Service 层、Controller 层 的公共方法
- ✅ 结合
extraTags添加业务维度 - ✅ 启用
publishPercentileHistogram以支持 Prometheus - ✅ 避免在 private 或 高频 方法上使用
通过 Micrometer 注解,你可以以极低的成本实现方法级监控,大幅提升应用的可观测性,是构建现代云原生应用的重要工具。
📌 官方文档参考:
9238

被折叠的 条评论
为什么被折叠?



