1. 简介
Micrometer 是一个简单的外观层,为多种主流监控系统提供统一的指标采集客户端。目前支持以下监控系统:Atlas、Datadog、Graphite、Ganglia、Influx、JMX 和 Prometheus。
本文将介绍 Micrometer 的基础用法及其与 Spring 的集成。为简化演示,我们以 Micrometer Atlas 为例说明大部分用例。
2. Maven 依赖
首先在 pom.xml
中添加以下依赖:
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-registry-atlas</artifactId>
<version>1.12.3</version>
</dependency>
最新版本可在 这里 查询。
3. MeterRegistry
在 Micrometer 中,MeterRegistry 是注册指标的核心组件。通过遍历注册表,我们可以将指标及其维度值组合生成后端时间序列数据。
最简单的注册表是 SimpleMeterRegistry,但实际使用时应选择针对监控系统的专用实现(如 Atlas 对应 AtlasMeterRegistry)。
CompositeMeterRegistry 允许同时添加多个注册表,实现指标向多个监控系统的同步发布:
CompositeMeterRegistry compositeRegistry = new CompositeMeterRegistry();
SimpleMeterRegistry oneSimpleMeter = new SimpleMeterRegistry();
AtlasMeterRegistry atlasMeterRegistry
= new AtlasMeterRegistry(atlasConfig, Clock.SYSTEM);
compositeRegistry.add(oneSimpleMeter);
compositeRegistry.add(atlasMeterRegistry);
Micrometer 还提供全局静态注册表 Metrics.globalRegistry,并通过 Metrics 的静态构建器生成指标:
@Test
public void givenGlobalRegistry_whenIncrementAnywhere_thenCounted() {
class CountedObject {
private CountedObject() {
Metrics.counter("objects.instance").increment(1.0);
}
}
Metrics.addRegistry(new SimpleMeterRegistry());
Metrics.counter("objects.instance").increment();
new CountedObject();
Optional<Counter> counterOptional = Optional.ofNullable(Metrics.globalRegistry
.find("objects.instance").counter());
assertTrue(counterOptional.isPresent());
assertTrue(counterOptional.get().count() == 2.0);
}
4. Tags 和 Meters
4.1. Tags
Meter 的标识由名称和标签组成。命名规范建议用点号分隔单词,确保指标名称跨监控系统的可移植性:
Counter counter = registry.counter("page.visitors", "age", "20s");
Tags 用于对指标进行切片分析。上例中 page.visitors
是指标名称,age=20s
是标签,表示统计 20-30 岁年龄段的页面访问量。
大型系统中可为注册表添加通用标签(如区域标识):
registry.config().commonTags("region", "ua-east");
4.2. Counter
Counter 用于统计应用属性的计数。可通过流式构建器或注册表辅助方法创建:
Counter counter = Counter
.builder("instance")
.description("indicates instance count of the object")
.tags("dev", "performance")
.register(registry);
counter.increment(2.0);
assertTrue(counter.count() == 2);
counter.increment(-1);
assertTrue(counter.count() == 1);
⚠️ 注意:Counter 只能单调递增,尝试递减操作会被忽略(如上例中 increment(-1)
实际未生效)。
4.3. Timers
使用 Timers 测量事件延迟或频率。Timer 至少会记录特定时间序列的总时间和事件数:
SimpleMeterRegistry registry = new SimpleMeterRegistry();
Timer timer = registry.timer("app.event");
timer.record(() -> {
try {
TimeUnit.MILLISECONDS.sleep(15);
} catch (InterruptedException ignored) {
}
});
timer.record(30, TimeUnit.MILLISECONDS);
assertTrue(2 == timer.count());
assertThat(timer.totalTime(TimeUnit.MILLISECONDS)).isBetween(40.0, 55.0);
对于长时间运行的任务,使用 LongTaskTimer:
SimpleMeterRegistry registry = new SimpleMeterRegistry();
LongTaskTimer longTaskTimer = LongTaskTimer
.builder("3rdPartyService")
.register(registry);
LongTaskTimer.Sample currentTaskId = longTaskTimer.start();
try {
TimeUnit.MILLISECONDS.sleep(2);
} catch (InterruptedException ignored) { }
long timeElapsed = currentTaskId.stop();
assertEquals(2L, timeElapsed/((int) 1e6),1L);
4.4. Gauge
Gauge 展示指标的瞬时值。与其他指标不同,Gauges 仅在观察时报告数据,适合监控缓存或集合状态:
SimpleMeterRegistry registry = new SimpleMeterRegistry();
List<String> list = new ArrayList<>(4);
Gauge gauge = Gauge
.builder("cache.size", list, List::size)
.register(registry);
assertTrue(gauge.value() == 0.0);
list.add("1");
assertTrue(gauge.value() == 1.0);
4.5. DistributionSummary
DistributionSummary 提供事件分布和简单摘要统计:
SimpleMeterRegistry registry = new SimpleMeterRegistry();
DistributionSummary distributionSummary = DistributionSummary
.builder("request.size")
.baseUnit("bytes")
.register(registry);
distributionSummary.record(3);
distributionSummary.record(4);
distributionSummary.record(5);
assertTrue(3 == distributionSummary.count());
assertTrue(12 == distributionSummary.totalAmount());
DistributionSummary 和 Timers 可通过百分位增强:
SimpleMeterRegistry registry = new SimpleMeterRegistry();
Timer timer = Timer
.builder("test.timer")
.publishPercentiles(0.3, 0.5, 0.95)
.publishPercentileHistogram()
.register(registry);
注册表会生成三个带标签 percentile=0.3
、percentile=0.5
、percentile=0.95
的 Gauge,分别表示 30%、50%、95% 观测值的阈值。添加记录后验证:
timer.record(2, TimeUnit.SECONDS);
timer.record(2, TimeUnit.SECONDS);
timer.record(3, TimeUnit.SECONDS);
timer.record(4, TimeUnit.SECONDS);
timer.record(8, TimeUnit.SECONDS);
timer.record(13, TimeUnit.SECONDS);
提取百分位值验证:
Map<Double, Double> actualMicrometer = new TreeMap<>();
ValueAtPercentile[] percentiles = timer.takeSnapshot().percentileValues();
for (ValueAtPercentile percentile : percentiles) {
actualMicrometer.put(percentile.percentile(), percentile.value(TimeUnit.MILLISECONDS));
}
Map<Double, Double> expectedMicrometer = new TreeMap<>();
expectedMicrometer.put(0.3, 1946.157056);
expectedMicrometer.put(0.5, 3019.89888);
expectedMicrometer.put(0.95, 13354.663936);
assertEquals(expectedMicrometer, actualMicrometer);
Micrometer 还支持 服务等级目标(直方图):
DistributionSummary hist = DistributionSummary
.builder("summary")
.serviceLevelObjectives(1, 10, 5)
.register(registry);
添加记录后验证直方图计算:
Map<Integer, Double> actualMicrometer = new TreeMap<>();
HistogramSnapshot snapshot = hist.takeSnapshot();
Arrays.stream(snapshot.histogramCounts()).forEach(p -> {
actualMicrometer.put((Integer.valueOf((int) p.bucket())), p.count());
});
Map<Integer, Double> expectedMicrometer = new TreeMap<>();
expectedMicrometer.put(1,0D);
expectedMicrometer.put(10,2D);
expectedMicrometer.put(5,1D);
assertEquals(expectedMicrometer, actualMicrometer);
直方图可直接对比不同分桶数据,还支持时间缩放(如分析后端服务响应时间):
Duration[] durations = {Duration.ofMillis(25), Duration.ofMillis(300), Duration.ofMillis(600)};
Timer timer = Timer
.builder("timer")
.sla(durations)
.publishPercentileHistogram()
.register(registry);
5. Binders
Micrometer 内置多种 Binders 监控 JVM、缓存、ExecutorService 和日志服务。
JVM 和系统监控支持:
- 类加载器指标 (ClassLoaderMetrics)
- JVM 内存池 (JvmMemoryMetrics)
- GC 指标 (JvmGcMetrics)
- 线程和 CPU 使用率 (JvmThreadMetrics, ProcessorMetrics)
缓存监控(支持 Guava、EhCache、Hazelcast、Caffeine):
日志服务监控通过绑定 LogbackMetrics 实现:
new LogbackMetrics().bind(registry);
其他 Binders 用法类似,此处不再赘述。
6. Spring 集成
Spring Boot Actuator 为 Micrometer 提供依赖管理和自动配置,支持 Spring Boot 2.0/1.x 和 Spring Framework 5.0/4.x。
添加以下依赖(最新版本见此处):
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-spring-legacy</artifactId>
<version>1.3.20</version>
</dependency>
无需修改现有代码即可启用 Spring 集成。JVM 内存指标将自动注册到全局注册表,并发布到默认 Atlas 接口:http://localhost:7101/api/v1/publish
。
可通过 spring.metrics.atlas.*
配置属性控制指标导出行为,完整配置项参考 AtlasConfig。
需绑定额外指标时,将其声明为 @Bean
即可。例如添加 JvmThreadMetrics:
@Bean
JvmThreadMetrics threadMetrics(){
return new JvmThreadMetrics();
}
Web 监控默认自动配置所有接口,可通过 spring.metrics.web.autoTimeServerRequests
管理。默认实现为接口提供四个维度的指标:HTTP 请求方法、HTTP 响应码、接口 URI 和异常信息。
请求响应后,Atlas 将发布请求方法(GET、POST 等)相关指标。通过 Atlas Graph API 可生成不同方法响应时间对比图:
默认还会报告 20x、30x、40x、50x 响应码:
可对比不同 URI:
或检查异常指标:
可通过 @Timed
注解在控制器类或方法上自定义标签、长任务、分位数和百分位:
@RestController
@Timed("people")
public class PeopleController {
@GetMapping("/people")
@Timed(value = "people.all", longTask = true)
public List<String> listPeople() {
//...
}
}
通过 Atlas 接口 http://localhost:7101/api/v1/tags/name
可查看以下标签:
["people", "people.all", "jvmBufferCount", ... ]
Micrometer 也支持 Spring Boot 2.0 的函数式 Web 框架,通过过滤 RouterFunction
启用指标:
RouterFunctionMetrics metrics = new RouterFunctionMetrics(registry);
RouterFunctions.route(...)
.filter(metrics.timer("server.requests"));
还可收集数据源和定时任务指标,详情参考官方文档。
7. 总结
本文介绍了指标门面 Micrometer。通过抽象并支持多种监控系统的通用语义,该工具使切换不同监控平台变得简单。
本文完整实现代码可在 GitHub 获取。