Spring Boot整合Prometheus案例

wen java案例 3

本文目录导读:

Spring Boot整合Prometheus案例

  1. 测试接口
  2. 模拟登录登出

我来为您提供一个完整的Spring Boot整合Prometheus的案例,包括监控指标采集、自定义指标和Grafana可视化。

项目依赖配置

Maven依赖 (pom.xml)

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
         http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.7.14</version>
        <relativePath/>
    </parent>
    <groupId>com.example</groupId>
    <artifactId>springboot-prometheus</artifactId>
    <version>1.0.0</version>
    <properties>
        <java.version>1.8</java.version>
        <micrometer.version>1.9.5</micrometer.version>
    </properties>
    <dependencies>
        <!-- Spring Boot Web -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!-- Actuator -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        <!-- Micrometer Prometheus Registry -->
        <dependency>
            <groupId>io.micrometer</groupId>
            <artifactId>micrometer-registry-prometheus</artifactId>
            <version>${micrometer.version}</version>
        </dependency>
        <!-- Lombok -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <!-- 数据库相关(示例使用) -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
        <dependency>
            <groupId>com.h2database</groupId>
            <artifactId>h2</artifactId>
            <scope>runtime</scope>
        </dependency>
        <!-- 测试 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <excludes>
                        <exclude>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                        </exclude>
                    </excludes>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

配置文件

application.yml

server:
  port: 8080
spring:
  application:
    name: springboot-prometheus-demo
  # H2数据库配置(示例)
  h2:
    console:
      enabled: true
      path: /h2-console
  datasource:
    url: jdbc:h2:mem:testdb
    driver-class-name: org.h2.Driver
    username: sa
    password: 
  jpa:
    hibernate:
      ddl-auto: create-drop
    show-sql: true
# Actuator配置
management:
  endpoints:
    web:
      exposure:
        include: "*"
        exclude: "env,beans"
  endpoint:
    health:
      show-details: always
      probes:
        enabled: true
    metrics:
      enabled: true
    prometheus:
      enabled: true
  metrics:
    export:
      prometheus:
        enabled: true
        step: 1m
        descriptions: true
    tags:
      application: ${spring.application.name}
    enable:
      jvm: true
      http: true
      logback: true
    distribution:
      percentiles-histogram:
        http.server.requests: true
      slo:
        http.server.requests: 10ms, 50ms, 100ms, 200ms, 500ms, 1s, 5s
      percentiles:
        http.server.requests: 0.5, 0.9, 0.95, 0.99

主应用类

package com.example.prometheus;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableScheduling;
@SpringBootApplication
@EnableScheduling
public class PrometheusApplication {
    public static void main(String[] args) {
        SpringApplication.run(PrometheusApplication.class, args);
    }
}

自定义指标示例

自定义指标注册器

package com.example.prometheus.metrics;
import io.micrometer.core.instrument.*;
import io.micrometer.core.instrument.binder.jvm.JvmMemoryMetrics;
import io.micrometer.core.instrument.simple.SimpleMeterRegistry;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
@Component
public class CustomMetrics {
    private final MeterRegistry meterRegistry;
    @Autowired
    public CustomMetrics(MeterRegistry meterRegistry) {
        this.meterRegistry = meterRegistry;
    }
    // 计数器示例
    public Counter requestCounter;
    // 仪表盘示例
    public Gauge activeUsers;
    // 直方图示例
    public Timer requestTimer;
    // 摘要示例
    public DistributionSummary responseSize;
    @PostConstruct
    public void init() {
        // 创建计数器
        requestCounter = Counter.builder("custom_requests_total")
                .description("Total number of requests")
                .tag("type", "custom")
                .register(meterRegistry);
        // 创建仪表盘(返回一个函数)
        AtomicInteger userCount = new AtomicInteger(0);
        activeUsers = Gauge.builder("custom_active_users", userCount, 
                AtomicInteger::get)
                .description("Current number of active users")
                .tag("type", "custom")
                .register(meterRegistry);
        // 创建计时器
        requestTimer = Timer.builder("custom_request_duration")
                .description("Request processing time")
                .publishPercentiles(0.5, 0.9, 0.95, 0.99)
                .publishPercentileHistogram()
                .sla(Duration.ofMillis(100), Duration.ofMillis(500))
                .register(meterRegistry);
        // 创建摘要
        responseSize = DistributionSummary.builder("custom_response_size")
                .description("Response size")
                .baseUnit("bytes")
                .register(meterRegistry);
    }
}

业务服务示例

package com.example.prometheus.service;
import io.micrometer.core.instrument.*;
import io.micrometer.core.instrument.search.Search;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.Random;
@Service
@Slf4j
public class OrderService {
    @Autowired
    private MeterRegistry meterRegistry;
    private final Random random = new Random();
    // 订单计数器
    private final Counter orderCounter;
    // 订单处理时间
    private final Timer orderTimer;
    // 活跃用户数
    private final AtomicInteger activeUsers = new AtomicInteger(0);
    public OrderService(MeterRegistry registry) {
        this.orderCounter = registry.counter("orders_total",  "type", "business");
        this.orderTimer = registry.timer("order_processing_time");
        // 注册仪表盘
        registry.gauge("active_users", activeUsers, AtomicInteger::get);
    }
    /**
     * 创建订单
     */
    public Order createOrder(OrderRequest request) {
        long startTime = System.currentTimeMillis();
        try {
            // 模拟业务处理
            Thread.sleep(random.nextInt(200));
            // 创建订单逻辑
            Order order = new Order();
            order.setId(System.currentTimeMillis());
            order.setName(request.getName());
            order.setAmount(request.getAmount());
            // 增加订单计数
            orderCounter.increment();
            // 记录订单数量
            meterRegistry.counter("orders_created_total", "type", "business", "status", "success")
                    .increment();
            log.info("Order created: {}", order.getId());
            return order;
        } catch (Exception e) {
            meterRegistry.counter("orders_created_total", "type", "business", "status", "failed")
                    .increment();
            log.error("Create order failed", e);
            throw new RuntimeException("Create order failed", e);
        } finally {
            // 记录订单处理时间
            long duration = System.currentTimeMillis() - startTime;
            orderTimer.record(Duration.ofMillis(duration));
            responseSize.record(random.nextInt(1024));
        }
    }
    /**
     * 用户登录
     */
    public void userLogin() {
        activeUsers.incrementAndGet();
        meterRegistry.counter("user_logins_total").increment();
    }
    /**
     * 用户登出
     */
    public void userLogout() {
        if (activeUsers.get() > 0) {
            activeUsers.decrementAndGet();
        }
    }
    /**
     * 获取活跃用户数
     */
    public int getActiveUsers() {
        return activeUsers.get();
    }
}

控制器示例

package com.example.prometheus.controller;
import com.example.prometheus.metrics.CustomMetrics;
import com.example.prometheus.service.OrderService;
import io.micrometer.core.annotation.Timed;
import io.micrometer.core.instrument.MeterRegistry;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
@RestController
@RequestMapping("/api")
@Slf4j
public class ApiController {
    @Autowired
    private OrderService orderService;
    @Autowired
    private CustomMetrics customMetrics;
    @Autowired
    private MeterRegistry meterRegistry;
    /**
     * 创建订单接口
     */
    @PostMapping("/orders")
    @Timed(value = "api.orders.create", extraTags = {"version", "v1"})
    public Map<String, Object> createOrder(@RequestBody OrderRequest request) {
        Map<String, Object> response = new HashMap<>();
        try {
            // 记录请求计数
            customMetrics.requestCounter.increment();
            // 计时器
            long startTime = System.nanoTime();
            // 调用服务
            Object order = orderService.createOrder(request);
            // 记录请求时间
            customMetrics.requestTimer.record(System.nanoTime() - startTime, TimeUnit.NANOSECONDS);
            response.put("success", true);
            response.put("data", order);
        } catch (Exception e) {
            response.put("success", false);
            response.put("error", e.getMessage());
        }
        // 记录响应大小
        customMetrics.responseSize.record(response.toString().length());
        return response;
    }
    /**
     * 用户登录
     */
    @PostMapping("/login")
    public String login(@RequestBody LoginRequest request) {
        orderService.userLogin();
        return "Login successful";
    }
    /**
     * 用户登出
     */
    @PostMapping("/logout")
    public String logout() {
        orderService.userLogout();
        return "Logout successful";
    }
    /**
     * 测试接口
     */
    @GetMapping("/test")
    public String test() {
        // 模拟不同的响应时间
        long startTime = System.currentTimeMillis();
        try {
            int delay = (int) (Math.random() * 300);
            Thread.sleep(delay);
        } catch (InterruptedException e) {
            log.error("Test endpoint interrupted", e);
            Thread.currentThread().interrupt();
        }
        customMetrics.requestCounter.increment();
        customMetrics.requestTimer.record(System.currentTimeMillis() - startTime, 
            TimeUnit.MILLISECONDS);
        return "Test endpoint - " + System.currentTimeMillis();
    }
    /**
     * 获取当前指标
     */
    @GetMapping("/metrics/status")
    public Map<String, Object> getMetricsStatus() {
        Map<String, Object> metrics = new HashMap<>();
        metrics.put("active_users", orderService.getActiveUsers());
        metrics.put("total_requests", customMetrics.requestCounter.count());
        return metrics;
    }
}
@Data
class OrderRequest {
    private String name;
    private Double amount;
}
@Data
class LoginRequest {
    private String username;
    private String password;
}

自定义注解支持

自定义计时器注解

package com.example.prometheus.annotation;
import java.lang.annotation.*;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Timed {
    String name() default "";
    String[] extraTags() default {};
}

AOP切面

package com.example.prometheus.aspect;
import io.micrometer.core.instrument.MeterRegistry;
import io.micrometer.core.instrument.Timer;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.concurrent.TimeUnit;
@Aspect
@Component
public class MetricsAspect {
    @Autowired
    private MeterRegistry meterRegistry;
    @Around("@annotation(timed)")
    public Object measureTime(ProceedingJoinPoint joinPoint, Timed timed) throws Throwable {
        String methodName = timed.name().isEmpty() 
                ? joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName()
                : timed.name();
        Timer timer = Timer.builder(methodName)
                .tags(timed.extraTags())
                .publishPercentiles(0.5, 0.9, 0.99)
                .register(meterRegistry);
        long start = System.nanoTime();
        try {
            return joinPoint.proceed();
        } finally {
            timer.record(System.nanoTime() - start, TimeUnit.NANOSECONDS);
        }
    }
}

Prometheus配置

prometheus.yml

# Prometheus配置文件
global:
  scrape_interval: 15s
  evaluation_interval: 15s
scrape_configs:
  - job_name: 'spring-boot-app'
    metrics_path: '/actuator/prometheus'
    static_configs:
      - targets: ['localhost:8080']
        labels:
          application: 'springboot-prometheus-demo'
          environment: 'development'
  - job_name: 'prometheus'
    static_configs:
      - targets: ['localhost:9090']

Docker Compose 配置

docker-compose.yml

version: '3.8'
services:
  # Spring Boot应用
  springboot-app:
    build: .
    container_name: springboot-prometheus-app
    ports:
      - "8080:8080"
    networks:
      - monitoring
  # Prometheus
  prometheus:
    image: prom/prometheus:latest
    container_name: prometheus
    ports:
      - "9090:9090"
    volumes:
      - ./prometheus.yml:/etc/prometheus/prometheus.yml
      - prometheus-data:/prometheus
    command:
      - '--config.file=/etc/prometheus/prometheus.yml'
      - '--storage.tsdb.path=/prometheus'
      - '--web.enable-lifecycle'
    networks:
      - monitoring
    depends_on:
      - springboot-app
  # Grafana
  grafana:
    image: grafana/grafana:latest
    container_name: grafana
    ports:
      - "3000:3000"
    environment:
      - GF_SECURITY_ADMIN_USER=admin
      - GF_SECURITY_ADMIN_PASSWORD=admin
      - GF_INSTALL_PLUGINS=grafana-clock-panel,grafana-simple-json-datasource
    volumes:
      - grafana-data:/var/lib/grafana
    networks:
      - monitoring
    depends_on:
      - prometheus
  # Node Exporter(可选)
  node-exporter:
    image: prom/node-exporter:latest
    container_name: node-exporter
    ports:
      - "9100:9100"
    networks:
      - monitoring
volumes:
  prometheus-data:
  grafana-data:
networks:
  monitoring:
    driver: bridge

Grafana Dashboard配置

dashboard.json (示例)

{
  "dashboard": {
    "id": null,: "Spring Boot Application Monitoring",
    "tags": ["spring-boot", "prometheus"],
    "timezone": "browser",
    "schemaVersion": 16,
    "version": 1,
    "refresh": "5s",
    "panels": [
      {
        "title": "HTTP Request Rate",
        "type": "graph",
        "gridPos": {"h": 8, "w": 12, "x": 0, "y": 0},
        "targets": [
          {
            "expr": "rate(http_server_requests_seconds_count[5m])",
            "legendFormat": "{{method}} - {{uri}}"
          }
        ]
      },
      {
        "title": "HTTP Request Duration",
        "type": "graph",
        "gridPos": {"h": 8, "w": 12, "x": 12, "y": 0},
        "targets": [
          {
            "expr": "histogram_quantile(0.95, sum(rate(http_server_requests_seconds_bucket[5m])) by (le))",
            "legendFormat": "P95"
          },
          {
            "expr": "histogram_quantile(0.99, sum(rate(http_server_requests_seconds_bucket[5m])) by (le))",
            "legendFormat": "P99"
          }
        ]
      },
      {
        "title": "JVM Memory Usage",
        "type": "graph",
        "gridPos": {"h": 8, "w": 12, "x": 0, "y": 8},
        "targets": [
          {
            "expr": "jvm_memory_used_bytes{jvm_area='heap'}",
            "legendFormat": "Heap"
          },
          {
            "expr": "jvm_memory_used_bytes{jvm_area='non-heap'}",
            "legendFormat": "Non-Heap"
          }
        ]
      },
      {
        "title": "Custom Metrics - Orders",
        "type": "singlestat",
        "gridPos": {"h": 4, "w": 6, "x": 0, "y": 16},
        "targets": [
          {
            "expr": "orders_total{type='business'}",
            "legendFormat": "Total Orders"
          }
        ],
        "valueName": "total"
      },
      {
        "title": "Request Counter",
        "type": "singlestat",
        "gridPos": {"h": 4, "w": 6, "x": 6, "y": 16},
        "targets": [
          {
            "expr": "custom_requests_total{type='custom'}",
            "legendFormat": "Total Requests"
          }
        ],
        "valueName": "current"
      }
    ]
  }
}

使用说明

测试步骤

  1. 启动应用

    mvn clean package
    java -jar target/springboot-prometheus-1.0.0.jar
  2. 访问Prometheus指标

  • 打开浏览器访问:http://localhost:8080/actuator/prometheus
  • 查看自定义指标:custom_requests_totalcustom_active_users
  1. 调用接口生成指标
    # 创建订单
    curl -X POST http://localhost:8080/api/orders \
    -H "Content-Type: application/json" \
    -d '{"name": "Test Order", "amount": 100.50}'

测试接口

curl http://localhost:8080/api/test

模拟登录登出

curl -X POST http://localhost:8080/api/login \ -H "Content-Type: application/json" \ -d '{"username": "user1", "password": "pass123"}'

curl -X POST http://localhost:8080/api/logout


4. **配置Prometheus**
- 启动Prometheus:`docker-compose up prometheus`
- 访问:`http://localhost:9090/targets`
5. **配置Grafana**
- 启动Grafana:`docker-compose up grafana`
- 访问:`http://localhost:3000`(用户名/密码:admin/admin)
- 添加数据源:Configuration → Data Sources → Add data source → Prometheus
- 设置URL为:`http://prometheus:9090`
## 11. 常见指标说明
| 指标名称 | 类型 | 说明 |
|---------|------|------|
| `http_server_requests_seconds` | Histogram | HTTP请求耗时 |
| `jvm_memory_used_bytes` | Gauge | JVM内存使用 |
| `custom_requests_total` | Counter | 自定义请求总数 |
| `custom_active_users` | Gauge | 活跃用户数 |
| `orders_created_total` | Counter | 创建的订单数 |
| `order_processing_time` | Timer | 订单处理时间 |
这个完整的案例展示了Spring Boot与Prometheus集成的各个方面,包括基础配置、自定义指标、监控数据采集和可视化,您可以根据实际需求进行扩展和调整。

抱歉,评论功能暂时关闭!