前言
Micrometer 是应用层指标的监控工具,为 Java 应用提供了统一的指标采集方式。本文将介绍 Micrometer 的核心概念和实战用法。
核心概念
1. 指标类型
| 类型 | 说明 | 使用场景 |
|---|---|---|
| Counter | 计数器,只增不减 | 请求次数、错误次数 |
| Gauge | 仪表盘,可增可减 | 内存使用、连接数 |
| Timer | 计时器,记录耗时 | 接口响应时间、方法执行时间 |
| DistributionSummary | 分布摘要,记录值分布 | 响应大小、订单金额 |
| LongTaskTimer | 长任务计时器 | 批处理任务、异步任务 |
2. 添加依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-registry-prometheus</artifactId>
</dependency>
3. 基础配置
management:
metrics:
enable:
all: true
tags:
application: ${spring.application.name}
environment: ${spring.profiles.active:dev}
endpoint:
prometheus:
enabled: true
指标使用
1. Counter 计数器
@Service
@RequiredArgsConstructor
public class OrderService {
private final MeterRegistry meterRegistry;
// 创建计数器
private final Counter orderCounter;
private final Counter errorCounter;
public OrderService(MeterRegistry meterRegistry) {
this.meterRegistry = meterRegistry;
this.orderCounter = Counter.builder("order.created")
.description("Number of orders created")
.tag("type", "online")
.register(meterRegistry);
this.errorCounter = Counter.builder("order.error")
.description("Number of order errors")
.tag("service", "order-service")
.register(meterRegistry);
}
public Order createOrder(OrderCreateDTO dto) {
try {
Order order = orderRepository.save(convert(dto));
// 增加计数
orderCounter.increment();
// 带标签计数
meterRegistry.counter("order.created",
"status", "success",
"type", dto.getType()
).increment();
return order;
} catch (Exception e) {
errorCounter.increment();
throw e;
}
}
}
2. Gauge 仪表盘
@Service
@RequiredArgsConstructor
public class ConnectionPoolService {
private final MeterRegistry meterRegistry;
private final DataSource dataSource;
@PostConstruct
public void init() {
// 注册连接池指标
Gauge.builder("db.pool.size", dataSource, ds -> {
try {
return ds.getConnection().getMetaData().getMaxConnections();
} catch (SQLException e) {
return 0;
}
})
.description("Database connection pool size")
.register(meterRegistry);
// 注册活跃连接数
Gauge.builder("db.pool.active", this, this::getActiveConnections)
.description("Active database connections")
.register(meterRegistry);
}
private double getActiveConnections() {
// 获取活跃连接数
return 10;
}
}
3. Timer 计时器
@Service
@RequiredArgsConstructor
public class UserService {
private final MeterRegistry meterRegistry;
/**
* 方式一:手动计时
*/
public UserDTO getUser(Long id) {
Timer.Sample sample = Timer.start(meterRegistry);
try {
User user = userRepository.findById(id).orElse(null);
return convertToDTO(user);
} finally {
sample.stop(Timer.builder("user.get.time")
.description("Time to get user")
.tag("operation", "findById")
.register(meterRegistry));
}
}
/**
* 方式二:record 方法
*/
public UserDTO getUserWithRecord(Long id) {
Timer timer = meterRegistry.timer("user.get.time");
return timer.record(() -> {
User user = userRepository.findById(id).orElse(null);
return convertToDTO(user);
});
}
/**
* 方式三:@Timed 注解
*/
@Timed(value = "user.create.time", description = "Time to create user")
public UserDTO createUser(UserCreateDTO dto) {
User user = convert(dto);
return convertToDTO(userRepository.save(user));
}
}
4. DistributionSummary 分布摘要
@Service
@RequiredArgsConstructor
public class FileService {
private final MeterRegistry meterRegistry;
private final DistributionSummary fileSizeSummary;
public FileService(MeterRegistry meterRegistry) {
this.meterRegistry = meterRegistry;
this.fileSizeSummary = DistributionSummary.builder("file.size")
.description("File size distribution")
.baseUnit("bytes")
.scale(1.0)
.serviceLevelObjectives(1024, 1024 * 1024, 10 * 1024 * 1024)
.register(meterRegistry);
}
public void uploadFile(MultipartFile file) {
long size = file.getSize();
// 记录文件大小
fileSizeSummary.record(size);
// 带标签记录
meterRegistry.summary("file.size",
"type", file.getContentType(),
"user", getCurrentUser()
).record(size);
// 保存文件
saveFile(file);
}
}
5. LongTaskTimer 长任务计时器
@Service
@RequiredArgsConstructor
public class BatchService {
private final MeterRegistry meterRegistry;
private final LongTaskTimer batchTimer;
public BatchService(MeterRegistry meterRegistry) {
this.meterRegistry = meterRegistry;
this.batchTimer = LongTaskTimer.builder("batch.process.time")
.description("Time for batch processing")
.register(meterRegistry);
}
public void processBatch() {
LongTaskTimer.Sample sample = batchTimer.start();
try {
// 长时间运行的任务
Thread.sleep(10000);
// 处理数据
processData();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
sample.stop();
}
}
}
自定义指标
1. 自定义 MeterBinder
@Component
public class CustomMetricsBinder implements MeterBinder {
private final DataSource dataSource;
public CustomMetricsBinder(DataSource dataSource) {
this.dataSource = dataSource;
}
@Override
public void bindTo(MeterRegistry registry) {
// 数据库连接数
Gauge.builder("custom.db.connections", this, this::getConnectionCount)
.description("Database connection count")
.register(registry);
// 缓存命中率
Gauge.builder("custom.cache.hit.ratio", this, this::getCacheHitRatio)
.description("Cache hit ratio")
.register(registry);
}
private double getConnectionCount() {
try {
Connection conn = dataSource.getConnection();
conn.close();
return 1;
} catch (SQLException e) {
return 0;
}
}
private double getCacheHitRatio() {
// 计算缓存命中率
return 0.95;
}
}
2. 业务指标
@Component
public class BusinessMetrics {
private final MeterRegistry meterRegistry;
public BusinessMetrics(MeterRegistry meterRegistry) {
this.meterRegistry = meterRegistry;
// 注册业务指标
registerOrderMetrics();
registerUserMetrics();
registerPaymentMetrics();
}
private void registerOrderMetrics() {
Counter.builder("business.order.total")
.description("Total orders")
.tag("business", "order")
.register(meterRegistry);
DistributionSummary.builder("business.order.amount")
.description("Order amount distribution")
.baseUnit("CNY")
.register(meterRegistry);
}
private void registerUserMetrics() {
Counter.builder("business.user.registered")
.description("Registered users")
.tag("business", "user")
.register(meterRegistry);
Gauge.builder("business.user.online", this, this::getOnlineUserCount)
.description("Online users")
.register(meterRegistry);
}
private void registerPaymentMetrics() {
Timer.builder("business.payment.time")
.description("Payment processing time")
.register(meterRegistry);
}
private double getOnlineUserCount() {
// 获取在线用户数
return 1000;
}
public void recordOrder(double amount) {
meterRegistry.counter("business.order.total").increment();
meterRegistry.summary("business.order.amount").record(amount);
}
public void recordUserRegistered() {
meterRegistry.counter("business.user.registered").increment();
}
public void recordPayment(long timeMs) {
meterRegistry.timer("business.payment.time")
.record(timeMs, TimeUnit.MILLISECONDS);
}
}
3. 指标标签
@Service
public class MetricService {
private final MeterRegistry meterRegistry;
public void recordRequest(String uri, String method, int status, long timeMs) {
Tags tags = Tags.of(
"uri", uri,
"method", method,
"status", String.valueOf(status)
);
// 记录请求次数
meterRegistry.counter("http.requests.total", tags).increment();
// 记录响应时间
meterRegistry.timer("http.requests.duration", tags)
.record(timeMs, TimeUnit.MILLISECONDS);
}
}
指标导出
1. Prometheus 导出
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-registry-prometheus</artifactId>
</dependency>
management:
metrics:
export:
prometheus:
enabled: true
2. InfluxDB 导出
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-registry-influx</artifactId>
</dependency>
management:
metrics:
export:
influx:
enabled: true
uri: http://localhost:8086
database: metrics
retention-policy: autogen
3. Elastic 导出
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-registry-elastic</artifactId>
</dependency>
management:
metrics:
export:
elastic:
enabled: true
host: http://localhost:9200
index: metrics
4. 多注册表
@Configuration
public class MetricsConfig {
@Bean
public MeterRegistry meterRegistry() {
CompositeMeterRegistry composite = new SimpleMeterRegistry();
// 添加 Prometheus
composite.add(new PrometheusMeterRegistry(PrometheusConfig.DEFAULT));
// 添加 InfluxDB
composite.add(new InfluxMeterRegistry(influxConfig(), Clock.SYSTEM));
return composite;
}
}
最佳实践
1. 指标命名规范
// ✅ 推荐 - 使用点分隔的命名
Counter.builder("order.created.total")
.tag("status", "success")
.register(registry);
// ❌ 不推荐 - 使用下划线或驼峰
Counter.builder("order_created_total")
.register(registry);
2. 标签使用
// ✅ 推荐 - 有限基数的标签
Counter.builder("http.requests")
.tag("method", "GET")
.tag("status", "200")
.register(registry);
// ❌ 不推荐 - 高基数标签(如 userId)
Counter.builder("user.requests")
.tag("userId", "12345") // 会导致指标爆炸
.register(registry);
3. 指标清理
@Component
public class MetricsCleaner implements ApplicationListener<ContextClosedEvent> {
private final MeterRegistry meterRegistry;
public MetricsCleaner(MeterRegistry meterRegistry) {
this.meterRegistry = meterRegistry;
}
@Override
public void onApplicationEvent(ContextClosedEvent event) {
// 清理指标
meterRegistry.getMeters().forEach(meterRegistry::remove);
}
}
4. 性能考虑
// ✅ 推荐 - 复用 Meter 对象
private final Counter orderCounter;
public OrderService(MeterRegistry registry) {
this.orderCounter = Counter.builder("order.created")
.register(registry);
}
public void createOrder() {
orderCounter.increment(); // 直接复用
}
// ❌ 不推荐 - 每次都创建
public void createOrder() {
Counter.builder("order.created")
.register(registry)
.increment();
}
总结
Micrometer 指标采集要点:
- ✅ 指标类型 - Counter、Gauge、Timer、DistributionSummary
- ✅ 自定义指标 - MeterBinder、业务指标
- ✅ 指标导出 - Prometheus、InfluxDB、Elastic
- ✅ 标签使用 - 有限基数、避免爆炸
- ✅ 最佳实践 - 命名规范、性能考虑
Micrometer 是应用监控的核心组件。