Skip to content
清晨的一缕阳光
返回

Spring Boot Micrometer 指标采集

前言

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 指标采集要点:

Micrometer 是应用监控的核心组件。


分享这篇文章到:

上一篇文章
Nacos 配置中心
下一篇文章
微服务测试策略