【性能监控 APM 六】Micrometer&actuator
SpringBoot 监控指标框架体系原理、核心组件、API、完整实战示例
一、整体体系概览
SpringBoot 监控体系核心:Spring Boot Actuator + Micrometer(指标门面)
历史版本区分
- SpringBoot1.x:Actuator底层直接使用Metrics(废弃)
- SpringBoot2.x+:Micrometer作为统一指标API门面(核心标准)
架构分层自上而下:
应用业务代码 → Micrometer API(统一指标接口)
↓
Micrometer实现(Prometheus/PrometheusMeterRegistry、Influx、JMX等)
↓
SpringBoot Actuator:对外暴露端点(/actuator/prometheus、/actuator/metrics)
↓
监控采集端:Prometheus → Grafana可视化;ELK;AlertManager告警
核心概念区分
- Actuator:提供运维端点(健康检查、指标、线程dump、环境信息),不是指标实现,是HTTP暴露层
- Micrometer:指标标准门面,类似SLF4j(日志门面);屏蔽不同监控系统API差异
- Meter:Micrometer中所有指标的统称,包含5大类型
- Registry(MeterRegistry):指标注册表,保存所有指标,对接具体监控系统
二、底层原理
1. Micrometer核心设计思想
门面模式
业务代码只依赖 io.micrometer 标准API,不需要绑定Prometheus/InfluxDB。切换监控系统只需要引入对应依赖、配置,业务代码零改动。
业务 → MeterRegistry(接口) ←———
|
PrometheusMeterRegistry / SimpleMeterRegistry / JmxMeterRegistry
SpringBoot自动装配机制:
MeterRegistryAutoConfiguration
自动注入全局MeterRegistry(CompositeMeterRegistry,组合多个Registry)
2. 自动指标采集(内置埋点)
SpringBoot自动注册大量内置指标,无需开发:
- JVM:堆内存、非堆内存、GC次数、GC耗时、线程数、类加载
- Tomcat/Undertow:http请求耗时、请求数量、错误码、活跃连接
- Spring MVC:接口调用指标
- JDBC(HikariCP):连接池活跃连接、等待队列、超时
- Logback:日志打印计数(error/warn/info)
3. Actuator工作原理
Actuator 通过Endpoint组件定义监控能力;
通过WebEndpointServlet/WebFlux适配器,将端点暴露为HTTP接口;
/actuator/health健康检查/actuator/metrics原始micrometer指标/actuator/prometheus适配Prometheus文本格式指标(最常用)
三、Meter五大指标类型(重点!属性与方法)
Micrometer 定义5种标准指标,每种指标适用场景严格区分
所有指标支持
Tag(标签)= 维度,用于多维度筛选(接口路径、异常类型、实例ip、环境)
| 指标类型 | 类 | 作用 | 典型场景 |
|---|---|---|---|
| Counter | 计数器 | 只增不减,仅记录总量 | 请求次数、错误次数、消息消费数量 |
| Gauge | 仪表盘 | 瞬时值,可增可减 | 当前活跃连接数、在线用户、队列长度、内存占用 |
| Timer | 计时器 | 记录事件耗时+调用次数 | HTTP接口耗时、方法执行耗时 |
| DistributionSummary | 分布摘要 | 记录大小分布(无时间维度) | 上传文件大小、报文长度 |
| LongTaskTimer | 长任务计时器 | 记录正在运行任务时长 | 异步任务、长时间运行任务(批处理) |
公共基础概念:Tag(维度标签)
Tag.of("uri", "/api/order/create");
Tag.of("status", "200");
// 推荐常量方式,避免字符串硬编码
Prometheus最佳实践:标签不要过多(基数爆炸cardinality风险!避免传入userId、orderId这类无限唯一值)
四、每种指标:核心API、属性、使用示例
前置Maven依赖(SpringBoot3.x,jakarta;2.x保持javax)
<!-- SpringBoot Actuator核心 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!-- prometheus格式导出(生产主流) -->
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-registry-prometheus</artifactId>
</dependency>
application.yml 基础配置
management:
endpoints:
web:
exposure:
include: health,prometheus,metrics
metrics:
tags:
application: ${spring.application.name} #全局标签:应用名
endpoint:
health:
show-details: always
1. Counter 计数器
特点:只能increment(),不能减少;适合统计总量
核心方法
// 创建
Counter counter = registry.counter("order.create.total", Tags.of("status", "success"));
// +1
counter.increment();
// +n
counter.increment(5);
// 获取当前值
counter.count();
完整使用示例
import io.micrometer.core.instrument.Counter;
import io.micrometer.core.instrument.MeterRegistry;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
@Service
public class OrderService {
@Resource
private MeterRegistry meterRegistry;
public void createOrder(Boolean success) {
Tags tags = Tags.of("status", success ? "success" : "fail");
Counter counter = meterRegistry.counter("order.create.count", tags);
counter.increment();
}
}
2. Gauge 仪表盘(瞬时指标)
特点:获取瞬时数值,不自动记录变化;适合表达当前状态
⚠️重点坑:Gauge不会主动回调,registry定时轮询取值;不要使用频繁创建对象
两种注册方式
方式1:Lambda 动态取值(推荐)
// 模拟队列
private final Queue<String> taskQueue = new ConcurrentLinkedQueue<>();
// Bean初始化时注册一次
@PostConstruct
public void registerGauge() {
meterRegistry.gauge("task.queue.size", Tags.empty(), taskQueue, Queue::size);
}
方式2:AtomicReference包装可变数值
AtomicInteger onlineUser = new AtomicInteger(0);
meterRegistry.gauge("online.user.count", Tags.empty(), onlineUser, AtomicInteger::get);
onlineUser.set(120);
禁止写法:
// ❌错误!每次调用新建Gauge,内存泄漏
public void test(){
meterRegistry.gauge("xxx", Tags.empty(), ()-> getValue());
}
3. Timer 计时器(最常用)
特点:统计事件耗时、调用次数,自动生成:总次数、平均耗时、最大最小、百分位p95/p99
两种使用模式
模式1:record(Runnable) 自动计时
Timer timer = meterRegistry.timer("order.pay.time", Tags.of("payType", "alipay"));
timer.record(this::payOrder);
模式2:手动控制开始结束
Timer.Sample sample = Timer.start(meterRegistry);
try {
payOrder();
} finally {
sample.stop(timer);
}
开启百分位配置(yml)
management.metrics.distribution.percentiles-histogram.order.pay.time: true
开启后Prometheus可以计算p95、p99延迟
4. DistributionSummary 分布摘要
无时间维度,统计数据大小(报文大小、文件尺寸)
DistributionSummary summary = meterRegistry.summary("http.response.size", Tags.of("uri", "/api/file/upload"));
// 记录数值(单位byte)
summary.record(1024);
summary.record(2048);
5. LongTaskTimer 长耗时任务
监控正在执行的任务,区分普通Timer(普通Timer任务结束才记录)
指标包含:活跃任务数、正在运行任务总时长
LongTaskTimer taskTimer = meterRegistry.longTaskTimer("batch.task.running", Tags.of("taskName", "sync_data"));
LongTaskTimer.Sample sample = taskTimer.start();
try{
//长时间批任务
}finally {
sample.stop();
}
五、三种埋点方案对比(业务开发可选)
方案1:原生Micrometer API(上面示例,灵活可控)
方案2:AOP + @Timed 注解(无侵入,推荐接口监控)
依赖spring-aop,直接注解方法自动生成Timer指标
import io.micrometer.core.annotation.Timed;
@Service
public class OrderService {
@Timed(value = "order.create.method.time", percentiles = {0.5,0.95,0.99})
public void createOrder() {
//业务逻辑
}
}
⚠️默认SpringBoot只对Controller生效!Service需要手动开启TimedAop
@Configuration
@EnableAspectJAutoProxy
public class MetricConfig {
@Bean
public TimedAspect timedAspect(MeterRegistry registry) {
return new TimedAspect(registry);
}
}
方案3:自动采集(零代码)
SpringMVC接口、连接池、JVM指标,不需要写任何代码,启动自动采集
访问 http://127.0.0.1:8080/actuator/metrics/http.server.requests 查看web请求指标
六、Actuator端点详解
常用端点
| 端点 | 作用 |
|---|---|
| /actuator/health | 健康检查,存活探测(k8s liveness/readiness) |
| /actuator/prometheus | 输出prometheus格式指标【生产必开】 |
| /actuator/metrics | 原始json格式指标(调试用) |
| /actuator/info | 应用版本信息 |
访问地址示例:
http://localhost:8080/actuator/prometheus
七、完整工程Demo结构
MetricConfig 统一指标配置(全局通用标签、百分位、过滤)
import io.micrometer.core.instrument.MeterRegistry;
import io.micrometer.core.instrument.config.MeterFilter;
import org.springframework.boot.actuate.autoconfigure.metrics.MeterRegistryCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class MetricConfig {
// 全局统一tag
@Bean
public MeterRegistryCustomizer<MeterRegistry> metricsCommonTags() {
return registry -> registry.config()
.commonTags("application", "order-service");
}
// 开启所有timer直方图(可按需限制指标名)
@Bean
public MeterRegistryCustomizer<MeterRegistry> enablePercentile() {
return registry -> registry.config()
.meterFilter(MeterFilter.maxExpected("http.server.requests", Duration.ofSeconds(5)))
.meterFilter(MeterFilter.publishPercentileHistogram());
}
}
八、生产常见坑 & 最佳实践
1. 基数爆炸(Cardinality 严重问题)
❌错误:tag放入唯一值
//禁止!每个订单一个tag,指标无限膨胀
Tags.of("orderId",orderId.toString())
✅正确:tag使用有限枚举:status、errorCode、uri、payType
2. Gauge 频繁创建导致内存泄漏
Gauge注册后不要循环创建,放在@PostConstruct一次性注册
3. Timer百分位不生效
需要开启 publishPercentileHistogram=true,否则prometheus无法计算p95/p99
4. /actuator/prometheus 404
yml没有暴露端点:
management.endpoints.web.exposure.include: health,prometheus
5. 区分SimpleMeterRegistry vs PrometheusMeterRegistry
- SimpleMeterRegistry:内存指标,重启丢失(单元测试)
- PrometheusMeterRegistry:适配Prometheus文本格式,生产使用
九、完整监控链路演示
应用(SpringBoot) → /actuator/prometheus
→ Prometheus定时拉取指标
→ Grafana配置图表(延迟、QPS、错误率、JVM内存)
→ AlertManager配置告警(错误率>5%、接口p99延迟过高)
十、拓展方向(需要我可以补充完整代码)
- Prometheus + Grafana 监控面板模板导入
- 自定义健康指示器 HealthIndicator
- 线程池、Redis模板自动埋点封装
- 全局统一异常指标AOP封装
- SpringBoot3 与 SpringBoot2 Micrometer差异
- 指标推拉模式(PushGateway主动推送 vs Prometheus拉取)
如果你需要,我可以直接提供一套可复制:全局接口监控AOP、异常计数器、线程池指标埋点完整工具类。
本文来自博客园,作者:蓝迷梦,转载请注明原文链接:https://www.cnblogs.com/hewei-blogs/articles/22048194

浙公网安备 33010602011771号