Micrometer 实战案例详解
案例背景
假设我们要为一个电商订单服务添加可观测性,需要监控:

- 订单创建速率
- 订单处理耗时
- 订单状态分布
- 数据库连接池状态
- JVM 指标
环境准备
<!-- pom.xml -->
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-core</artifactId>
<version>1.12.5</version>
</dependency>
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-registry-prometheus</artifactId>
<version>1.12.5</version>
</dependency>
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-registry-jmx</artifactId>
<version>1.12.5</version>
</dependency>
基础配置
@Configuration
public class MicrometerConfig {
@Bean
public MeterRegistry meterRegistry() {
// 自定义 Registry 组合
CompositeMeterRegistry compositeRegistry = new CompositeMeterRegistry();
// Prometheus Registry
PrometheusMeterRegistry prometheusRegistry = new PrometheusMeterRegistry(
PrometheusConfig.DEFAULT
);
prometheusRegistry.config().commonTags("application", "order-service");
// JMX Registry
JmxMeterRegistry jmxRegistry = new JmxMeterRegistry(
JmxConfig.DEFAULT,
Clock.SYSTEM
);
compositeRegistry.add(prometheusRegistry);
compositeRegistry.add(jmxRegistry);
// 配置默认标签
compositeRegistry.config()
.commonTags("environment", "production")
.commonTags("region", "cn-north-1");
return compositeRegistry;
}
@Bean
public PrometheusMeterRegistry prometheusRegistry() {
return new PrometheusMeterRegistry(PrometheusConfig.DEFAULT);
}
}
核心业务指标埋点
1 计数器 Counter - 订单量统计
@Service
public class OrderService {
private final Counter orderCounter;
private final Counter failedOrderCounter;
private final MeterRegistry meterRegistry;
public OrderService(MeterRegistry meterRegistry) {
this.meterRegistry = meterRegistry;
// 创建计数器 - 总订单数
this.orderCounter = Counter.builder("order.total")
.description("Total number of orders")
.tags("type", "all")
.register(meterRegistry);
// 创建计数器 - 失败订单数
this.failedOrderCounter = Counter.builder("order.failed")
.description("Number of failed orders")
.register(meterRegistry);
}
public Order createOrder(OrderRequest request) {
try {
// 业务逻辑
Order order = doCreateOrder(request);
// 计数器自增
orderCounter.increment();
// 按省份统计
meterRegistry.counter("order.total",
"region", request.getRegion(),
"type", request.getOrderType()
).increment();
return order;
} catch (Exception e) {
failedOrderCounter.increment();
throw e;
}
}
/**
* 测试不同数值范围
*/
@PostConstruct
public void init() {
// 模拟批量创建订单计数(测试用途)
for (int i = 0; i < 100; i++) {
orderCounter.increment(1.5); // 支持小数增量
}
// 1.5 * 100 = 150
orderCounter.increment(50);
}
}
2 仪表盘 Gauge - 实时状态
@Service
public class OrderStatusService {
private final Map<String, Integer> orderStatusMap = new ConcurrentHashMap<>();
private final MeterRegistry meterRegistry;
public OrderStatusService(MeterRegistry meterRegistry) {
this.meterRegistry = meterRegistry;
initGuages();
}
private void initGuages() {
// 创建 Gauge - 订单状态数量
Gauge.builder("order.status.count", orderStatusMap,
map -> map.getOrDefault("PENDING", 0))
.description("Current pending orders")
.tag("status", "PENDING")
.register(meterRegistry);
Gauge.builder("order.status.count", orderStatusMap,
map -> map.getOrDefault("PROCESSING", 0))
.description("Current processing orders")
.tag("status", "PROCESSING")
.register(meterRegistry);
// 订单金额统计(无需额外线程)
Gauge.builder("order.amount.total", this,
OrderStatusService::getTotalAmount)
.description("Total order amount")
.register(meterRegistry);
}
private double getTotalAmount() {
return totalAmount.get();
}
private final AtomicLong totalAmount = new AtomicLong(0);
public void updateOrderStatus(String orderId, String status) {
// 更新数量
orderStatusMap.merge(status, 1, Integer::sum);
// 更新金额
totalAmount.addAndGet(getOrderAmount(orderId));
}
}
3 计时器 Timer - 耗时监控
@Service
public class OrderProcessingService {
private final Timer orderProcessTimer;
private final Timer dbQueryTimer;
private final MeterRegistry meterRegistry;
public OrderProcessingService(MeterRegistry meterRegistry) {
this.meterRegistry = meterRegistry;
// 订单处理计时器
this.orderProcessTimer = Timer.builder("order.process.time")
.description("Time taken to process order")
.publishPercentileHistogram() // 百分位直方图
.publishPercentiles(0.5, 0.95, 0.99) // 特定百分位
.register(meterRegistry);
// 数据库查询计时器
this.dbQueryTimer = Timer.builder("db.query.time")
.description("Database query execution time")
.register(meterRegistry);
}
// 方式一:手动计时
public void processOrder(String orderId) {
Timer.Sample sample = Timer.start(meterRegistry);
try {
// 订单处理逻辑
TimeUnit.MILLISECONDS.sleep(200); // 模拟耗时
processOrderInternal(orderId);
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
// 记录耗时
sample.stop(orderProcessTimer);
}
}
// 方式二:使用 runnable/callable
public void processOrderWithTimer(String orderId) {
orderProcessTimer.record(() -> {
// 订单处理逻辑
try {
TimeUnit.MILLISECONDS.sleep(300);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
});
}
// 方式三:记录指定持续时间
public void recordOrderDuration(long durationInMillis) {
orderProcessTimer.record(durationInMillis, TimeUnit.MILLISECONDS);
}
}
4 长任务计时器 LongTaskTimer - 长期运行任务
@Service
public class BatchProcessingService {
private final LongTaskTimer longTaskTimer;
public BatchProcessingService(MeterRegistry meterRegistry) {
// 批量处理计时器
this.longTaskTimer = LongTaskTimer.builder("batch.processing")
.description("Batch processing task duration")
.register(meterRegistry);
}
public void processBatch(List<Order> orders) {
// 开始计时
LongTaskTimer.Sample sample = longTaskTimer.start();
try {
// 处理数据 - 模拟长时间运行
for (Order order : orders) {
Thread.sleep(100); // 每个订单处理100ms
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
// 停止计时
sample.stop();
}
}
}
5 分发摘要 DistributionSummary - 分布统计
@Service
public class OrderPriceService {
private final DistributionSummary orderAmountSummary;
public OrderPriceService(MeterRegistry meterRegistry) {
// 订单金额分布
this.orderAmountSummary = DistributionSummary.builder("order.amount")
.description("Order amount distribution")
.baseUnit("CNY")
.publishPercentileHistogram()
.publishPercentiles(0.5, 0.95, 0.99)
.register(meterRegistry);
}
public void recordOrderAmount(double amount) {
orderAmountSummary.record(amount);
// 按订单类型单独记录
meterRegistry.summary("order.amount")
.record(amount);
}
}
自定义绑定器
@Component
public class CustomMetricsBinder implements MeterBinder {
@Override
public void bindTo(MeterRegistry registry) {
// 定义自定义指标
AtomicInteger activeSessions = new AtomicInteger(0);
Gauge.builder("app.active.sessions", activeSessions, AtomicInteger::get)
.description("Active user sessions")
.tag("type", "web")
.register(registry);
// 自定义功能指标
registry.counter("app.error.count", "type", "auth").increment();
// 自定义计时器
Timer timer = Timer.builder("app.request.latency")
.tags("method", "get", "endpoint", "/api/order")
.register(registry);
}
}
完整监控示例
@RestController
@RequestMapping("/api/orders")
public class OrderController {
private final MeterRegistry meterRegistry;
private final OrderService orderService;
public OrderController(MeterRegistry meterRegistry, OrderService orderService) {
this.meterRegistry = meterRegistry;
this.orderService = orderService;
}
@PostMapping
public Order createOrder(@RequestBody OrderRequest request) {
// 记录请求计数
meterRegistry.counter("api.requests",
"method", "POST",
"endpoint", "/api/orders"
).increment();
Timer.Sample sample = Timer.start(meterRegistry);
try {
// 创建订单
Order order = orderService.createOrder(request);
// 记录成功状态
meterRegistry.counter("api.requests.success",
"endpoint", "/api/orders"
).increment();
return order;
} catch (Exception e) {
// 记录异常
meterRegistry.counter("api.requests.error",
"endpoint", "/api/orders",
"error_type", e.getClass().getSimpleName()
).increment();
throw e;
} finally {
// 记录耗时
sample.stop(meterRegistry.timer("api.request.duration",
"method", "POST",
"endpoint", "/api/orders"
));
}
}
}
Spring Boot 集成示例
@SpringBootApplication
@EnableScheduling
public class OrderServiceApplication {
public static void main(String[] args) {
SpringApplication.run(OrderServiceApplication.class, args);
}
@Bean
public PrometheusMeterRegistry prometheusRegistry() {
return new PrometheusMeterRegistry(PrometheusConfig.DEFAULT);
}
@Bean
public MeterRegistryCustomizer<MeterRegistry> metricsCommonTags() {
return registry -> registry.config()
.commonTags("application", "order-service")
.commonTags("environment", "production");
}
@Bean
public TimedAspect timedAspect(MeterRegistry registry) {
return new TimedAspect(registry);
}
}
测试示例
@SpringBootTest
class OrderServiceMetricsTest {
@Autowired
private MeterRegistry meterRegistry;
@Test
void testCounterIncrement() {
// 获取计数器
Counter counter = meterRegistry.counter("order.total");
// 记录增量
counter.increment(10);
// 验证计数
assertEquals(10, counter.count());
}
@Test
void testTimerRecording() {
Timer timer = meterRegistry.timer("test.timer");
// 记录耗时
timer.record(100, TimeUnit.MILLISECONDS);
timer.record(Duration.ofMillis(200));
// 验证
assertEquals(2, timer.count());
assertTrue(timer.totalTime(TimeUnit.MILLISECONDS) >= 300);
}
@Test
void testSummary() {
DistributionSummary summary = meterRegistry.summary("test.summary");
summary.record(10.5);
summary.record(20.3);
assertEquals(30.8, summary.totalAmount(), 0.01);
assertEquals(2, summary.count());
}
}
Prometheus 输出格式
配置完成后,访问 /actuator/prometheus 可以看到:
# HELP order_total Total number of orders
# TYPE order_total counter
order_total{application="order-service",environment="production",} 150.0
# HELP order_process_time Time taken to process order
# TYPE order_process_time summary
order_process_time_count{quantile="0.5",} 5.0
order_process_time_sum{quantile="0.5",} 1234.0
最佳实践总结
1 命名规范
- 使用小写字母和点号分隔
- 前缀表示模块:
order.、user.、db. - 后缀表示类型:
_total、_duration、_max
2 标签使用
// 好的标签
timer = meterRegistry.timer("order.processing",
"status", "success",
"region", "cn-north"
);
// 避免高基数标签(如订单ID)
// 不要使用订单ID作为标签!
3 性能考虑
// 缓存 Meter 实例,避免重复创建
private final Timer timer;
// 在构造器中创建
// 使用定时上报而不是每次请求都上报
@Scheduled(fixedDelay = 60000)
public void reportMetrics() {
// 定期上报指标
}
4 监控告警
@EventListener
public void handleOrderFailedEvent(OrderFailedEvent event) {
// 触发告警
if (failedOrderCounter.count() > 100) {
// 发送告警通知
alertService.sendAlert("订单失败次数超过100");
}
}
十一、常见问题处理
// 处理线程安全
private final AtomicInteger concurrentOrders = new AtomicInteger(0);
@PostConstruct
public void setup() {
Gauge.builder("order.concurrent", concurrentOrders, AtomicInteger::get)
.register(meterRegistry);
}
// 动态标签(避免)
public void recordOrderMeter(String orderId) {
// 不要这样使用,会导致高基数
meterRegistry.counter("order.detail", "orderId", orderId).increment();
// 应该使用固定标签
meterRegistry.counter("order.detail").increment();
}
这个案例涵盖了 Micrometer 的大部分核心功能,你可以根据实际需求选择使用,记得根据监控系统的要求配置对应的 Registry。