百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术文章 > 正文

SpringBoot探针实现:从零构建应用健康监控利器

itomcoil 2025-07-06 12:57 2 浏览

SpringBoot探针实现:从零构建应用健康监控利器

声明

本文中的所有案例代码、配置仅供参考,如需使用请严格做好相关测试及评估,对于因参照本文内容进行操作而导致的任何直接或间接损失,作者概不负责。本文旨在通过生动易懂的方式分享实用技术知识,欢迎读者就技术观点进行交流与指正。

引言部分

在微服务架构日益普及的今天,应用的健康状态监控成为了开发者面临的重要挑战。当您的SpringBoot应用部署在生产环境中时,您是否遇到过这样的困扰:

  • 应用突然无响应,但不知道具体哪个组件出现了问题?
  • 数据库连接池耗尽,却无法及时发现和预警?
  • 内存使用率飙升,但缺乏有效的监控手段?
  • 第三方服务异常,影响了整个应用的可用性?

这些问题的根源在于缺乏有效的应用探针机制。探针(Probe)作为现代应用监控的核心组件,能够实时检测应用的健康状态,为运维和开发团队提供关键的决策依据。

本文将带您从零开始,深入理解探针的工作原理,并手把手实现一个功能完整的SpringBoot探针系统。通过本文的学习,您将掌握:

  • 探针的核心概念和分类
  • SpringBoot Actuator的深度应用
  • 自定义健康检查指标的实现
  • 探针数据的可视化展示
  • 生产环境的最佳实践

背景知识

什么是探针?

探针(Probe)是一种轻量级的监控机制,用于定期检查应用程序或系统组件的健康状态。在云原生和微服务架构中,探针扮演着"哨兵"的角色,持续监控应用的各项指标,确保服务的可用性和稳定性。

探针的发展历程

探针概念最初源于网络监控领域,随着容器化技术的发展,Kubernetes将探针概念引入到容器编排中。SpringBoot框架通过Actuator模块,为Java应用提供了完善的探针支持。

探针的核心原理

探针的工作原理基于"主动检测"模式:

  1. 定期执行:按照预设的时间间隔执行检查逻辑
  2. 状态评估:根据检查结果判断组件健康状态
  3. 结果报告:将检查结果以标准格式返回
  4. 异常处理:当检测到异常时触发相应的处理机制

探针的分类

根据检测目标和用途,探针主要分为以下几类:

  1. 存活探针(Liveness Probe)

目的:检测应用是否正在运行

失败处理:重启应用实例

典型场景:检测死锁、内存泄漏等致命问题

  1. 就绪探针(Readiness Probe)

目的:检测应用是否准备好接收流量

失败处理:从负载均衡中移除实例

典型场景:应用启动过程、依赖服务不可用

  1. 启动探针(Startup Probe)

目的:检测应用是否已完成启动

失败处理:延长启动超时时间

典型场景:大型应用的缓慢启动过程

问题分析

传统监控方案的局限性

在探针技术出现之前,应用监控主要依赖以下方式:

  1. 日志监控:通过分析应用日志判断系统状态

局限性:被动监控,问题发现滞后

挑战:日志量大,分析复杂

  1. 外部监控:通过第三方工具定期访问应用接口

局限性:只能检测表面状态,无法深入内部组件

挑战:网络延迟影响检测准确性

  1. 手动检查:运维人员定期手动检查系统状态

局限性:效率低下,容易遗漏

挑战:无法实现7×24小时监控

现代应用监控的核心挑战

  1. 多层次监控需求

应用层:业务逻辑健康状态

中间件层:数据库、缓存、消息队列连接状态

基础设施层:CPU、内存、磁盘使用情况

  1. 实时性要求

故障快速发现:秒级检测异常状态

自动恢复:基于探针结果触发自愈机制

预警机制:在问题恶化前提前告警

  1. 标准化挑战

统一接口:不同组件的健康检查接口标准化

数据格式:监控数据的统一格式和传输协议

集成复杂性:与现有监控系统的无缝集成

探针工作原理流程图

上图展示了探针的完整工作流程,从启动初始化到循环检查,再到异常处理的全过程。探针通过持续的循环检查机制,确保能够及时发现和响应应用状态的变化。

关键技术挑战的本质

  1. 检查逻辑的设计复杂性

如何设计既全面又高效的检查逻辑?

如何平衡检查频率与系统性能开销?

如何处理检查过程中的异常情况?

  1. 状态判断的准确性

如何避免误报和漏报?

如何处理瞬时异常与持续异常?

如何设置合理的超时和重试机制?

解决方案详解

SpringBoot Actuator:探针的技术基础

SpringBoot Actuator是Spring官方提供的生产就绪特性模块,为应用提供了丰富的监控和管理功能。它是实现探针的理想技术基础。

Actuator的核心优势

  1. 开箱即用:提供多种预定义的健康检查指标
  2. 高度可扩展:支持自定义健康检查逻辑
  3. 标准化接口:遵循REST API设计规范
  4. 安全可控:提供细粒度的访问控制机制

SpringBoot探针架构图

上图展示了SpringBoot探针的整体架构,包括Actuator模块、各种健康检查组件以及与外部监控系统的集成关系。通过这种架构设计,我们可以实现全方位的应用监控。

核心组件设计

我们的探针系统将包含以下核心组件:

  1. 健康检查管理器:统一管理所有健康检查逻辑
  2. 自定义健康指标:针对特定业务场景的检查逻辑
  3. 指标数据收集器:收集和聚合各种监控指标
  4. 告警通知服务:异常状态的通知机制
  5. 配置管理模块:动态配置检查参数

实践案例

项目结构设计

首先,让我们设计一个完整的项目结构来实现我们的探针系统:

probe-demo/
├── src/
│   └── main/
│       ├── java/
│       │   └── 包名称,请自行替换/
│       │       ├── ProbeApplication.java
│       │       ├── config/
│       │       │   ├── ActuatorConfig.java
│       │       │   └── ProbeConfig.java
│       │       ├── health/
│       │       │   ├── CustomHealthIndicator.java
│       │       │   ├── DatabaseHealthChecker.java
│       │       │   ├── RedisHealthChecker.java
│       │       │   └── BusinessHealthChecker.java
│       │       ├── metrics/
│       │       │   ├── CustomMetrics.java
│       │       │   └── MetricsCollector.java
│       │       ├── service/
│       │       │   ├── HealthCheckService.java
│       │       │   └── NotificationService.java
│       │       └── controller/
│       │           └── ProbeController.java
│       └── resources/
│           ├── application.yml
│           └── application-prod.yml
├── pom.xml
└── README.md

依赖配置

首先创建完整的Maven依赖配置:

<?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>

    <groupId>包名称,请自行替换</groupId>
    <artifactId>springboot-probe-demo</artifactId>
    <version>1.0.0</version>
    <packaging>jar</packaging>

    <name>SpringBoot Probe Demo</name>
    <description>SpringBoot探针实现示例项目</description>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <spring-boot.version>2.7.0</spring-boot.version>
    </properties>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>${spring-boot.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <dependencies>
        <!-- SpringBoot核心依赖 -->
        <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>

        <!-- 数据库相关依赖 -->
        <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>

        <!-- Redis依赖 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>

        <!-- Micrometer指标依赖 -->
        <dependency>
            <groupId>io.micrometer</groupId>
            <artifactId>micrometer-registry-prometheus</artifactId>
        </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>
                <version>${spring-boot.version}</version>
                <executions>
                    <execution>
                        <goals>
                            <goal>repackage</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
</project>

应用配置

创建application.yml配置文件:

# 应用基础配置
server:
  port: 8080
  servlet:
    context-path: /probe-demo

spring:
  application:
    name: springboot-probe-demo

  # 数据库配置(使用H2内存数据库进行演示)
  datasource:
    url: jdbc:h2:mem:testdb
    driver-class-name: org.h2.Driver
    username: sa
    password:

  # JPA配置
  jpa:
    hibernate:
      ddl-auto: create-drop
    show-sql: true
    database-platform: org.hibernate.dialect.H2Dialect

  # H2控制台配置
  h2:
    console:
      enabled: true
      path: /h2-console

  # Redis配置(此处可自行连接Redis进行验证)
  redis:
    host: localhost
    port: 6379
    timeout: 2000ms
    lettuce:
      pool:
        max-active: 8
        max-idle: 8
        min-idle: 0

# Actuator配置
management:
  endpoints:
    web:
      exposure:
        # 暴露所有端点,生产环境请根据需要选择性暴露
        include: "*"
      base-path: /actuator
  endpoint:
    health:
      # 显示详细健康信息
      show-details: always
      # 显示组件信息
      show-components: always
    metrics:
      enabled: true
    prometheus:
      enabled: true

  # 健康检查配置
  health:
    # 启用磁盘空间检查
    diskspace:
      enabled: true
      threshold: 100MB
    # 启用数据库健康检查
    db:
      enabled: true
    # 启用Redis健康检查
    redis:
      enabled: true

# 自定义探针配置
probe:
  # 健康检查配置
  health:
    # 检查间隔(秒)
    check-interval: 30
    # 超时时间(毫秒)
    timeout: 5000
    # 重试次数
    retry-count: 3

  # 业务健康检查配置
  business:
    # 启用业务健康检查
    enabled: true
    # 关键业务服务列表
    critical-services:
      - user-service
      - order-service
      - payment-service

  # 告警配置
  alert:
    # 启用告警
    enabled: true
    # 告警邮箱(如有需要自行替换)
    email: 「邮箱地址,自行替换」
    # 告警阈值
    thresholds:
      cpu-usage: 80
      memory-usage: 85
      disk-usage: 90

# 日志配置
logging:
  level:
    包名称,请自行替换: DEBUG
    org.springframework.boot.actuate: INFO
  pattern:
    console: "%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n"

核心代码实现

1. 主启动类

package 包名称,请自行替换;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableScheduling;

/**
 * SpringBoot探针演示应用主启动类
 *
 * 安全提示:本示例代码仅供学习参考,生产环境使用前请进行充分测试
 *
 * @author 开发者
 * @version 1.0.0
 */
@SpringBootApplication
@EnableScheduling  // 启用定时任务支持
public class ProbeApplication {

    public static void main(String[] args) {
        SpringApplication.run(ProbeApplication.class, args);
        System.out.println("=================================");
        System.out.println("SpringBoot探针演示应用启动成功!");
        System.out.println("健康检查地址: http://localhost:8080/probe-demo/actuator/health");
        System.out.println("指标监控地址: http://localhost:8080/probe-demo/actuator/metrics");
        System.out.println("Prometheus地址: http://localhost:8080/probe-demo/actuator/prometheus");
        System.out.println("=================================");
    }
}

2. 自定义健康检查指标

package 包名称,请自行替换.health;

import org.springframework.boot.actuate.health.Health;
import org.springframework.boot.actuate.health.HealthIndicator;
import org.springframework.stereotype.Component;
import java.lang.management.ManagementFactory;
import java.lang.management.MemoryMXBean;
import java.lang.management.OperatingSystemMXBean;

/**
 * 自定义系统资源健康检查指标
 *
 * 检查CPU使用率、内存使用率等系统资源状态
 *
 * 安全提示:生产环境中请根据实际情况调整阈值参数
 */
@Component("systemResource")
public class CustomHealthIndicator implements HealthIndicator {

    // CPU使用率阈值(百分比)
    private static final double CPU_THRESHOLD = 80.0;
    // 内存使用率阈值(百分比)
    private static final double MEMORY_THRESHOLD = 85.0;

    @Override
    public Health health() {
        try {
            // 获取系统信息
            OperatingSystemMXBean osBean = ManagementFactory.getOperatingSystemMXBean();
            MemoryMXBean memoryBean = ManagementFactory.getMemoryMXBean();

            // 获取CPU使用率(注意:某些JVM实现可能返回-1)
            double cpuUsage = osBean.getProcessCpuLoad() * 100;
            if (cpuUsage < 0) {
                cpuUsage = 0; // 如果无法获取,设为0
            }

            // 计算内存使用率
            long usedMemory = memoryBean.getHeapMemoryUsage().getUsed();
            long maxMemory = memoryBean.getHeapMemoryUsage().getMax();
            double memoryUsage = (double) usedMemory / maxMemory * 100;

            // 构建健康检查结果
            Health.Builder healthBuilder = new Health.Builder();

            // 添加详细信息
            healthBuilder.withDetail("cpu_usage_percent", String.format("%.2f", cpuUsage))
                        .withDetail("memory_usage_percent", String.format("%.2f", memoryUsage))
                        .withDetail("used_memory_mb", usedMemory / 1024 / 1024)
                        .withDetail("max_memory_mb", maxMemory / 1024 / 1024)
                        .withDetail("available_processors", osBean.getAvailableProcessors());

            // 判断健康状态
            if (cpuUsage > CPU_THRESHOLD || memoryUsage > MEMORY_THRESHOLD) {
                return healthBuilder.down()
                    .withDetail("reason", "系统资源使用率过高")
                    .withDetail("cpu_threshold", CPU_THRESHOLD)
                    .withDetail("memory_threshold", MEMORY_THRESHOLD)
                    .build();
            }

            return healthBuilder.up()
                .withDetail("status", "系统资源状态正常")
                .build();

        } catch (Exception e) {
            return Health.down()
                .withDetail("error", "获取系统信息失败: " + e.getMessage())
                .build();
        }
    }
}

3. 数据库健康检查器

package 包名称,请自行替换.health;

import org.springframework.boot.actuate.health.Health;
import org.springframework.boot.actuate.health.HealthIndicator;
import org.springframework.stereotype.Component;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;

/**
 * 数据库连接健康检查器
 *
 * 检查数据库连接状态和响应时间
 *
 * 安全提示:生产环境中请使用连接池监控,避免频繁创建连接
 */
@Component("database")
public class DatabaseHealthChecker implements HealthIndicator {

    private final DataSource dataSource;

    // 数据库响应时间阈值(毫秒)
    private static final long RESPONSE_TIME_THRESHOLD = 1000;

    public DatabaseHealthChecker(DataSource dataSource) {
        this.dataSource = dataSource;
    }

    @Override
    public Health health() {
        try {
            long startTime = System.currentTimeMillis();

            // 执行简单的数据库查询测试连接
            try (Connection connection = dataSource.getConnection();
                 PreparedStatement statement = connection.prepareStatement("SELECT 1");
                 ResultSet resultSet = statement.executeQuery()) {

                long responseTime = System.currentTimeMillis() - startTime;

                // 检查查询结果
                if (resultSet.next() && resultSet.getInt(1) == 1) {
                    Health.Builder healthBuilder = Health.up()
                        .withDetail("database", "连接正常")
                        .withDetail("response_time_ms", responseTime)
                        .withDetail("connection_url", getMaskedUrl())
                        .withDetail("driver", connection.getMetaData().getDriverName());

                    // 检查响应时间
                    if (responseTime > RESPONSE_TIME_THRESHOLD) {
                        healthBuilder.withDetail("warning", "数据库响应时间较慢");
                    }

                    return healthBuilder.build();
                } else {
                    return Health.down()
                        .withDetail("database", "查询测试失败")
                        .withDetail("response_time_ms", responseTime)
                        .build();
                }
            }
        } catch (Exception e) {
            return Health.down()
                .withDetail("database", "连接失败")
                .withDetail("error", e.getMessage())
                .withDetail("error_type", e.getClass().getSimpleName())
                .build();
        }
    }

    /**
     * 获取脱敏的数据库URL
     * 安全提示:避免在健康检查结果中暴露敏感信息
     */
    private String getMaskedUrl() {
        try {
            try (Connection connection = dataSource.getConnection()) {
                String url = connection.getMetaData().getURL();
                // 简单脱敏处理,隐藏密码等敏感信息
                return url.replaceAll("password=[^&;]*", "password=***");
            }
        } catch (Exception e) {
            return "无法获取数据库URL";
        }
    }
}

4. 业务健康检查器

package 包名称,请自行替换.health;

import org.springframework.boot.actuate.health.Health;
import org.springframework.boot.actuate.health.HealthIndicator;
import org.springframework.stereotype.Component;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;

/**
 * 业务逻辑健康检查器
 *
 * 模拟检查关键业务服务的健康状态
 *
 * 安全提示:实际项目中应该调用真实的业务接口进行检查
 */
@Component("business")
public class BusinessHealthChecker implements HealthIndicator {

    private final Random random = new Random();

    // 模拟的业务服务列表
    private final String[] businessServices = {
        "用户服务", "订单服务", "支付服务", "库存服务", "通知服务"
    };

    @Override
    public Health health() {
        try {
            Map<String, Object> serviceStatus = new HashMap<>();
            boolean allServicesHealthy = true;
            int healthyCount = 0;
            int totalCount = businessServices.length;

            // 检查每个业务服务的状态(此处使用模拟数据)
            for (String service : businessServices) {
                boolean isHealthy = checkServiceHealth(service);
                serviceStatus.put(service, isHealthy ? "正常" : "异常");

                if (isHealthy) {
                    healthyCount++;
                } else {
                    allServicesHealthy = false;
                }
            }

            // 计算服务可用率
            double availabilityRate = (double) healthyCount / totalCount * 100;

            Health.Builder healthBuilder = new Health.Builder();
            healthBuilder.withDetail("total_services", totalCount)
                        .withDetail("healthy_services", healthyCount)
                        .withDetail("availability_rate", String.format("%.2f%%", availabilityRate))
                        .withDetail("service_details", serviceStatus)
                        .withDetail("check_time", System.currentTimeMillis());

            // 判断整体健康状态
            if (allServicesHealthy) {
                return healthBuilder.up()
                    .withDetail("status", "所有业务服务运行正常")
                    .build();
            } else if (availabilityRate >= 80) {
                return healthBuilder.up()
                    .withDetail("status", "部分业务服务异常,但整体可用")
                    .withDetail("warning", "请关注异常服务状态")
                    .build();
            } else {
                return healthBuilder.down()
                    .withDetail("status", "多个业务服务异常,影响系统可用性")
                    .withDetail("action_required", "需要立即处理异常服务")
                    .build();
            }

        } catch (Exception e) {
            return Health.down()
                .withDetail("error", "业务健康检查执行失败: " + e.getMessage())
                .build();
        }
    }

    /**
     * 模拟检查单个业务服务的健康状态
     * 实际项目中应该调用真实的服务接口或检查服务注册中心
     *
     * @param serviceName 服务名称
     * @return 服务是否健康
     */
    private boolean checkServiceHealth(String serviceName) {
        try {
            // 模拟网络延迟
            Thread.sleep(50 + random.nextInt(100));

            // 模拟90%的成功率
            return random.nextDouble() > 0.1;

        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            return false;
        }
    }
}

5. 自定义指标收集器

package 包名称,请自行替换.metrics;

import io.micrometer.core.instrument.Counter;
import io.micrometer.core.instrument.Gauge;
import io.micrometer.core.instrument.MeterRegistry;
import io.micrometer.core.instrument.Timer;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;

/**
 * 自定义指标收集器
 *
 * 收集应用的自定义业务指标
 *
 * 安全提示:指标收集不应影响业务性能,避免在热点代码中进行复杂计算
 */
@Component
public class CustomMetrics {

    private final MeterRegistry meterRegistry;

    // 业务指标计数器
    private final AtomicInteger activeUsers = new AtomicInteger(0);
    private final AtomicLong totalRequests = new AtomicLong(0);
    private final AtomicInteger errorCount = new AtomicInteger(0);

    // Micrometer指标
    private Counter requestCounter;
    private Counter errorCounter;
    private Timer responseTimer;

    public CustomMetrics(MeterRegistry meterRegistry) {
        this.meterRegistry = meterRegistry;
    }

    @PostConstruct
    public void initMetrics() {
        // 初始化计数器
        requestCounter = Counter.builder("app.requests.total")
            .description("应用请求总数")
            .tag("application", "probe-demo")
            .register(meterRegistry);

        errorCounter = Counter.builder("app.errors.total")
            .description("应用错误总数")
            .tag("application", "probe-demo")
            .register(meterRegistry);

        // 初始化计时器
        responseTimer = Timer.builder("app.response.time")
            .description("应用响应时间")
            .tag("application", "probe-demo")
            .register(meterRegistry);

        // 注册Gauge指标
        Gauge.builder("app.users.active")
            .description("当前活跃用户数")
            .tag("application", "probe-demo")
            .register(meterRegistry, this, CustomMetrics::getActiveUsers);

        Gauge.builder("app.requests.current")
            .description("当前请求总数")
            .tag("application", "probe-demo")
            .register(meterRegistry, this, CustomMetrics::getTotalRequests);
    }

    /**
     * 记录请求
     */
    public void recordRequest() {
        requestCounter.increment();
        totalRequests.incrementAndGet();
    }

    /**
     * 记录错误
     */
    public void recordError() {
        errorCounter.increment();
        errorCount.incrementAndGet();
    }

    /**
     * 记录响应时间
     */
    public Timer.Sample startTimer() {
        return Timer.start(meterRegistry);
    }

    /**
     * 增加活跃用户
     */
    public void incrementActiveUsers() {
        activeUsers.incrementAndGet();
    }

    /**
     * 减少活跃用户
     */
    public void decrementActiveUsers() {
        activeUsers.decrementAndGet();
    }

    // Getter方法供Gauge使用
    public double getActiveUsers() {
        return activeUsers.get();
    }

    public double getTotalRequests() {
        return totalRequests.get();
    }

    public int getErrorCount() {
        return errorCount.get();
    }
}

6. 探针控制器

package 包名称,请自行替换.controller;

import 包名称,请自行替换.metrics.CustomMetrics;
import org.springframework.boot.actuate.health.HealthComponent;
import org.springframework.boot.actuate.health.HealthEndpoint;
import org.springframework.web.bind.annotation.*;
import java.util.HashMap;
import java.util.Map;

/**
 * 探针控制器
 *
 * 提供自定义的探针接口和演示功能
 *
 * 安全提示:生产环境中应该添加适当的访问控制和限流机制
 */
@RestController
@RequestMapping("/api/probe")
public class ProbeController {

    private final HealthEndpoint healthEndpoint;
    private final CustomMetrics customMetrics;

    public ProbeController(HealthEndpoint healthEndpoint, CustomMetrics customMetrics) {
        this.healthEndpoint = healthEndpoint;
        this.customMetrics = customMetrics;
    }

    /**
     * 获取简化的健康状态
     */
    @GetMapping("/health/simple")
    public Map<String, Object> getSimpleHealth() {
        HealthComponent health = healthEndpoint.health();
        Map<String, Object> result = new HashMap<>();

        result.put("status", health.getStatus().getCode());
        result.put("timestamp", System.currentTimeMillis());
        result.put("application", "probe-demo");

        return result;
    }

    /**
     * 获取详细的健康状态
     */
    @GetMapping("/health/detailed")
    public HealthComponent getDetailedHealth() {
        return healthEndpoint.health();
    }

    /**
     * 模拟用户登录(增加活跃用户数)
     */
    @PostMapping("/simulate/login")
    public Map<String, Object> simulateLogin() {
        customMetrics.recordRequest();
        customMetrics.incrementActiveUsers();

        Map<String, Object> result = new HashMap<>();
        result.put("message", "用户登录成功");
        result.put("activeUsers", customMetrics.getActiveUsers());
        result.put("timestamp", System.currentTimeMillis());

        return result;
    }

    /**
     * 模拟用户登出(减少活跃用户数)
     */
    @PostMapping("/simulate/logout")
    public Map<String, Object> simulateLogout() {
        customMetrics.recordRequest();
        customMetrics.decrementActiveUsers();

        Map<String, Object> result = new HashMap<>();
        result.put("message", "用户登出成功");
        result.put("activeUsers", customMetrics.getActiveUsers());
        result.put("timestamp", System.currentTimeMillis());

        return result;
    }

    /**
     * 模拟错误(增加错误计数)
     */
    @PostMapping("/simulate/error")
    public Map<String, Object> simulateError() {
        customMetrics.recordRequest();
        customMetrics.recordError();

        Map<String, Object> result = new HashMap<>();
        result.put("message", "模拟错误已记录");
        result.put("errorCount", customMetrics.getErrorCount());
        result.put("timestamp", System.currentTimeMillis());

        return result;
    }

    /**
     * 获取应用统计信息
     */
    @GetMapping("/stats")
    public Map<String, Object> getStats() {
        Map<String, Object> stats = new HashMap<>();
        stats.put("activeUsers", customMetrics.getActiveUsers());
        stats.put("totalRequests", customMetrics.getTotalRequests());
        stats.put("errorCount", customMetrics.getErrorCount());
        stats.put("timestamp", System.currentTimeMillis());

        return stats;
    }
}

测试用例实现

7. 健康检查测试类

package 包名称,请自行替换;

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.web.client.TestRestTemplate;
import org.springframework.boot.web.server.LocalServerPort;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import static org.junit.jupiter.api.Assertions.*;

/**
 * 探针功能测试类
 *
 * 测试各种健康检查和指标收集功能
 *
 * 安全提示:测试用例应该覆盖各种异常场景,确保探针的可靠性
 */
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class ProbeApplicationTests {

    @LocalServerPort
    private int port;

    @Autowired
    private TestRestTemplate restTemplate;

    /**
     * 测试应用启动
     */
    @Test
    public void contextLoads() {
        // 应用能够正常启动即表示测试通过
        assertTrue(true, "应用启动成功");
    }

    /**
     * 测试健康检查端点
     */
    @Test
    public void testHealthEndpoint() {
        String url = "http://localhost:" + port + "/probe-demo/actuator/health";
        ResponseEntity<String> response = restTemplate.getForEntity(url, String.class);

        assertEquals(HttpStatus.OK, response.getStatusCode());
        assertNotNull(response.getBody());
        assertTrue(response.getBody().contains("status"));

        System.out.println("健康检查响应: " + response.getBody());
    }

    /**
     * 测试指标端点
     */
    @Test
    public void testMetricsEndpoint() {
        String url = "http://localhost:" + port + "/probe-demo/actuator/metrics";
        ResponseEntity<String> response = restTemplate.getForEntity(url, String.class);

        assertEquals(HttpStatus.OK, response.getStatusCode());
        assertNotNull(response.getBody());
        assertTrue(response.getBody().contains("names"));

        System.out.println("指标列表响应: " + response.getBody());
    }

    /**
     * 测试自定义探针接口
     */
    @Test
    public void testCustomProbeEndpoints() {
        // 测试简单健康检查
        String healthUrl = "http://localhost:" + port + "/probe-demo/api/probe/health/simple";
        ResponseEntity<String> healthResponse = restTemplate.getForEntity(healthUrl, String.class);
        assertEquals(HttpStatus.OK, healthResponse.getStatusCode());

        // 测试统计信息
        String statsUrl = "http://localhost:" + port + "/probe-demo/api/probe/stats";
        ResponseEntity<String> statsResponse = restTemplate.getForEntity(statsUrl, String.class);
        assertEquals(HttpStatus.OK, statsResponse.getStatusCode());

        System.out.println("自定义健康检查响应: " + healthResponse.getBody());
        System.out.println("统计信息响应: " + statsResponse.getBody());
    }

    /**
     * 测试模拟功能
     */
    @Test
    public void testSimulationEndpoints() {
        String baseUrl = "http://localhost:" + port + "/probe-demo/api/probe/simulate";

        // 测试模拟登录
        ResponseEntity<String> loginResponse = restTemplate.postForEntity(
            baseUrl + "/login", null, String.class);
        assertEquals(HttpStatus.OK, loginResponse.getStatusCode());

        // 测试模拟登出
        ResponseEntity<String> logoutResponse = restTemplate.postForEntity(
            baseUrl + "/logout", null, String.class);
        assertEquals(HttpStatus.OK, logoutResponse.getStatusCode());

        // 测试模拟错误
        ResponseEntity<String> errorResponse = restTemplate.postForEntity(
            baseUrl + "/error", null, String.class);
        assertEquals(HttpStatus.OK, errorResponse.getStatusCode());

        System.out.println("模拟登录响应: " + loginResponse.getBody());
        System.out.println("模拟登出响应: " + logoutResponse.getBody());
        System.out.println("模拟错误响应: " + errorResponse.getBody());
    }
}

运行环境与操作流程

运行环境说明

适用环境:

  • Java 8或更高版本
  • SpringBoot 2.7.0或更高版本
  • Maven 3.6或更高版本
  • 开发环境:IDEA、Eclipse或VSCode
  • 运行方式:IDE运行、命令行运行或打包部署

系统要求:

  • 内存:至少512MB可用内存
  • 磁盘:至少100MB可用空间
  • 网络:如需连接外部Redis,确保网络连通性

完整运行指南

步骤1:环境准备

  1. 确认Java环境
java -version
# 应显示Java 8或更高版本
  1. 确认Maven环境
mvn -version
# 应显示Maven 3.6或更高版本

步骤2:项目创建与配置

  1. 创建项目目录
mkdir springboot-probe-demo
cd springboot-probe-demo
  1. 创建Maven项目结构
mkdir -p src/main/java/包名称,请自行替换/{config,health,metrics,service,controller}
mkdir -p src/main/resources
mkdir -p src/test/java/包名称,请自行替换
  1. 复制配置文件

将上述pom.xml内容保存到项目根目录

将application.yml内容保存到src/main/resources目录

步骤3:代码实现

按照上述代码示例,依次创建以下文件:

  • ProbeApplication.java(主启动类)
  • CustomHealthIndicator.java(自定义健康检查)
  • DatabaseHealthChecker.java(数据库健康检查)
  • BusinessHealthChecker.java(业务健康检查)
  • CustomMetrics.java(自定义指标)
  • ProbeController.java(探针控制器)
  • ProbeApplicationTests.java(测试类)

步骤4:依赖下载与编译

  1. 下载依赖
mvn clean compile
# 首次运行会下载所有依赖,请耐心等待
  1. 编译项目
mvn package -DskipTests
# 跳过测试进行快速编译

步骤5:启动应用

方式一:IDE运行

  • 在IDE中右键运行ProbeApplication.java的main方法

方式二:Maven命令运行

mvn spring-boot:run

方式三:JAR包运行

java -jar target/springboot-probe-demo-1.0.0.jar

步骤6:验证运行结果

  1. 检查启动日志 应看到类似以下输出:
=================================
SpringBoot探针演示应用启动成功!
健康检查地址: http://localhost:8080/probe-demo/actuator/health
指标监控地址: http://localhost:8080/probe-demo/actuator/metrics
Prometheus地址: http://localhost:8080/probe-demo/actuator/prometheus
=================================
  1. 访问健康检查端点
curl http://localhost:8080/probe-demo/actuator/health

预期响应:

{
  "status": "UP",
  "components": {
    "business": {
      "status": "UP",
      "details": {
        "total_services": 5,
        "healthy_services": 4,
        "availability_rate": "80.00%"
      }
    },
    "database": {
      "status": "UP",
      "details": {
        "database": "连接正常",
        "response_time_ms": 45
      }
    },
    "systemResource": {
      "status": "UP",
      "details": {
        "cpu_usage_percent": "15.23",
        "memory_usage_percent": "45.67"
      }
    }
  }
}

步骤7:功能测试

  1. 运行单元测试
mvn test
  1. 测试自定义接口
# 获取简化健康状态
curl http://localhost:8080/probe-demo/api/probe/health/simple

# 模拟用户登录
curl -X POST http://localhost:8080/probe-demo/api/probe/simulate/login

# 获取统计信息
curl http://localhost:8080/probe-demo/api/probe/stats

探针数据流向图

上图展示了探针系统的完整数据流向,从客户端请求到各个组件的交互过程,帮助理解探针的工作机制。

常见问题与解决方案

问题1:应用启动失败

现象: 启动时出现端口占用错误

Port 8080 was already in use

解决方案:

  1. 检查端口占用:netstat -an | grep 8080
  2. 修改配置文件中的端口:server.port: 8081
  3. 或者停止占用端口的进程

问题2:健康检查返回DOWN状态

现象: 访问健康检查端点返回状态为DOWN

排查步骤:

  1. 检查应用日志中的错误信息
  2. 验证数据库连接配置是否正确
  3. 确认Redis连接是否可用(如果配置了Redis)
  4. 检查自定义健康检查逻辑是否有异常

解决方案:

# 查看详细健康信息
curl http://localhost:8080/probe-demo/actuator/health | jq .

# 检查特定组件状态
curl http://localhost:8080/probe-demo/actuator/health/db

问题3:指标数据无法获取

现象: 访问metrics端点返回空数据或404错误

解决方案:

  1. 确认Actuator依赖已正确添加
  2. 检查application.yml中的endpoints配置
  3. 验证Micrometer依赖是否正确

问题4:内存使用率过高

现象: 系统资源健康检查显示内存使用率超过阈值

解决方案:

  1. 调整JVM内存参数:java -Xmx1g -jar app.jar
  2. 优化应用代码,减少内存占用
  3. 调整健康检查阈值配置

性能优化建议

  1. 健康检查频率优化

根据业务需求调整检查间隔

避免过于频繁的检查影响性能

  1. 指标收集优化

选择性收集关键指标

使用异步方式收集复杂指标

  1. 缓存机制

对检查结果进行适当缓存

避免重复的昂贵检查操作

进阶优化

集成外部监控系统

Prometheus集成

我们的探针系统已经内置了Prometheus支持,可以直接与Prometheus监控系统集成:

  1. Prometheus配置
# prometheus.yml
global:
  scrape_interval: 15s

scrape_configs:
  - job_name: 'springboot-probe'
    static_configs:
      - targets: ['localhost:8080']
    metrics_path: '/probe-demo/actuator/prometheus'
    scrape_interval: 30s
  1. 启动Prometheus
# 下载并启动Prometheus
./prometheus --config.file=prometheus.yml
  1. 访问Prometheus界面 打开 http://localhost:9090 查看收集的指标数据

监控系统集成架构图

上图展示了完整的监控系统集成架构,包括应用集群、数据收集、可视化展示和告警通知的完整链路。

Grafana仪表板配置

创建Grafana仪表板来可视化探针数据:

{
  "dashboard": {
    "title": "SpringBoot探针监控仪表板",
    "panels": [
      {
        "title": "应用健康状态",
        "type": "stat",
        "targets": [
          {
            "expr": "up{job=\"springboot-probe\"}",
            "legendFormat": "应用状态"
          }
        ]
      },
      {
        "title": "活跃用户数",
        "type": "graph",
        "targets": [
          {
            "expr": "app_users_active",
            "legendFormat": "活跃用户"
          }
        ]
      },
      {
        "title": "请求总数",
        "type": "graph",
        "targets": [
          {
            "expr": "rate(app_requests_total[5m])",
            "legendFormat": "请求速率"
          }
        ]
      },
      {
        "title": "错误率",
        "type": "graph",
        "targets": [
          {
            "expr": "rate(app_errors_total[5m]) / rate(app_requests_total[5m]) * 100",
            "legendFormat": "错误率(%)"
          }
        ]
      }
    ]
  }
}

告警规则配置

Prometheus告警规则

创建告警规则文件 alert_rules.yml

groups:
  - name: springboot-probe-alerts
    rules:
      - alert: ApplicationDown
        expr: up{job="springboot-probe"} == 0
        for: 1m
        labels:
          severity: critical
        annotations:
          summary: "SpringBoot应用实例宕机"
          description: "应用实例 {{ $labels.instance }} 已宕机超过1分钟"

      - alert: HighErrorRate
        expr: rate(app_errors_total[5m]) / rate(app_requests_total[5m]) * 100 > 5
        for: 2m
        labels:
          severity: warning
        annotations:
          summary: "应用错误率过高"
          description: "应用错误率为 {{ $value }}%,超过5%阈值"

      - alert: HighMemoryUsage
        expr: jvm_memory_used_bytes{area="heap"} / jvm_memory_max_bytes{area="heap"} * 100 > 85
        for: 5m
        labels:
          severity: warning
        annotations:
          summary: "内存使用率过高"
          description: "JVM堆内存使用率为 {{ $value }}%,超过85%阈值"

扩展思路与优化方向

1. 分布式探针架构

对于微服务架构,可以考虑以下扩展:

  1. 服务注册与发现集成

与Eureka、Consul等注册中心集成

自动发现和监控新的服务实例

  1. 链路追踪集成

集成Zipkin或Jaeger进行分布式链路追踪

监控服务间调用的健康状态

  1. 配置中心集成

与Spring Cloud Config集成

动态调整探针配置参数

2. 智能化监控

  1. 机器学习异常检测

基于历史数据训练异常检测模型

自动识别异常模式和趋势

  1. 自适应阈值

根据历史数据动态调整告警阈值

减少误报和漏报

  1. 预测性维护

基于趋势分析预测潜在问题

提前进行预防性维护

3. 安全性增强

  1. 访问控制

实现基于角色的访问控制

保护敏感的监控数据

  1. 数据脱敏

对敏感信息进行脱敏处理

确保监控数据的安全性

  1. 审计日志

记录所有监控操作的审计日志

支持合规性要求

潜在问题及解决策略

1. 性能影响问题

问题描述: 频繁的健康检查可能影响应用性能

解决策略:

  • 合理设置检查间隔,避免过于频繁
  • 使用异步检查机制,避免阻塞主线程
  • 实现检查结果缓存,减少重复计算
  • 监控探针本身的性能开销

2. 误报问题

问题描述: 网络抖动或瞬时异常导致的误报

解决策略:

  • 实现重试机制和容错逻辑
  • 设置合理的超时时间和重试次数
  • 使用滑动窗口算法平滑异常检测
  • 区分瞬时异常和持续异常

3. 扩展性问题

问题描述: 随着服务数量增加,监控复杂度急剧上升

解决策略:

  • 采用分层监控架构
  • 实现监控配置的自动化管理
  • 使用标签和分组机制组织监控对象
  • 建立监控数据的生命周期管理

适用场景与局限性分析

适用场景

  1. 微服务架构

服务数量众多,需要统一监控

服务间依赖复杂,需要全链路监控

  1. 云原生应用

容器化部署,需要动态监控

弹性伸缩,需要自适应监控

  1. 关键业务系统

高可用性要求,需要实时监控

故障影响大,需要快速发现和恢复

局限性分析

  1. 资源消耗

监控本身会消耗系统资源

需要在监控精度和性能之间平衡

  1. 复杂性管理

监控配置和规则管理复杂

需要专业的运维团队维护

  1. 数据存储

监控数据量大,存储成本高

需要合理的数据保留策略

总结与展望

核心要点回顾

通过本文的学习,我们深入了解了SpringBoot探针的实现原理和实践方法:

  1. 探针基础概念

探针是现代应用监控的核心组件

分为存活探针、就绪探针和启动探针三类

基于主动检测模式,提供实时的健康状态信息

  1. 技术实现要点

SpringBoot Actuator提供了完善的探针基础设施

自定义HealthIndicator可以实现特定的业务检查逻辑

Micrometer集成支持多种监控系统的指标收集

  1. 最佳实践原则

合理设置检查频率和超时时间

实现分层监控和渐进式告警

注重监控数据的安全性和隐私保护

  1. 集成扩展能力

与Prometheus、Grafana等监控系统无缝集成

支持分布式环境下的服务发现和监控

具备良好的扩展性和可定制性

探针技术发展趋势图

上图展示了探针技术从传统监控到智能化监控的发展历程,以及未来的技术趋势。

技术趋势展望

1. 可观测性的全面发展

未来的探针技术将朝着全栈可观测性方向发展:

  • 统一观测平台:整合指标、日志、链路追踪三大支柱
  • 业务可观测性:从技术指标扩展到业务指标监控
  • 用户体验监控:关注最终用户的真实体验

2. AI驱动的智能监控

人工智能技术将深度融入探针系统:

  • 异常检测算法:基于机器学习的智能异常识别
  • 根因分析:自动分析故障根本原因
  • 预测性维护:提前预测和预防潜在问题

学习资源推荐

官方文档

  • Spring Boot Actuator官方文档
  • Micrometer官方文档
  • Prometheus官方文档

开源项目

  • Spring Boot Admin:SpringBoot应用监控界面
  • Grafana:开源监控可视化平台
  • AlertManager:Prometheus告警管理器

学习路径建议

  1. 基础阶段:掌握SpringBoot Actuator的基本使用
  2. 进阶阶段:学习自定义健康检查和指标收集
  3. 高级阶段:实现分布式监控和智能告警
  4. 专家阶段:设计企业级监控架构和最佳实践

更多文章一键直达

冷不叮的小知识

相关推荐

zabbix企业微信告警(zabbix5.0企业微信告警详细)

zabbix企业微信告警的前提是用户有企业微信且创建了一个能够发送消息的应用,具体怎么创建可以协同用户侧企业微信的管理员。第一步:企业微信准备我们需要的内容包括企业ID,应用的AgentId和应用的S...

基于centos7部署saltstack服务器管理自动化运维平台

概述SaltStack是一个服务器基础架构集中化管理平台,具备配置管理、远程执行、监控等功能,基于Python语言实现,结合轻量级消息队列(ZeroMQ)与Python第三方模块(Pyzmq、PyCr...

功能实用,效率提升,Python开发的自动化运维工具

想要高效的完成日常运维工作,不论是代码部署、应用管理还是资产信息录入,都需要一个自动化运维平台。今天我们分享一个开源项目,它可以帮助运维人员完成日常工作,提高效率,降低成本,它就是:OpsManage...

centos定时任务之python脚本(centos7执行python脚本)

一、crontab的安装默认情况下,CentOS7中已经安装有crontab,如果没有安装,可以通过yum进行安装。yuminstallcrontabs二、crontab的定时语法说明*代表取...

Fedora 41 终于要和 Python 2.7 说再见了

红帽工程师MiroHroncok提交了一份变更提案,建议在Fedora41中退役Python2.7,并放弃仍然依赖Python2的软件包。Python2已于2020年1...

软件测试|使用docker搞定 Python环境搭建

前言当我们在公司的电脑上搭建了一套我们需要的Python环境,比如我们的版本是3.8的Python,那我可能有一天换了一台电脑之后,我整套环境就需要全部重新搭建,不只是Python,我们一系列的第三方...

环境配置篇:Centos如何安装Python解释器

有小伙伴时常会使用Python进行编程,那么如何配置centos中的Python环境呢?1)先安装依赖yuminstallgccgcc-c++sqlite-devel在root用户下操作:1...

(三)Centos7.6安装MySql(centos8.3安装docker)

借鉴文章:centos7+django+python3+mysql+阿里云部署项目全流程。这里我只借鉴安装MySql这一部分。链接:https://blog.csdn.net/a394268045/a...

Centos7.9 如何安装最新版本的Docker

在CentOS7.9系统中安装最新版本的Docker,需遵循以下步骤,并注意依赖项的兼容性问题:1.卸载旧版本Docker(如已安装)若系统中存在旧版Docker,需先卸载以避免冲突:sudoy...

Linux 磁盘空间不够用?5 招快速清理文件,释放 10GB 空间不是梦!

刚收到服务器警告:磁盘空间不足90%!装软件提示Nospaceleftondevice!连日志都写不进去,系统卡到崩溃?别慌!今天教你5个超实用的磁盘清理大招,从临时文件到无用软件一键搞定...

Playwright软件测试框架学习笔记(playwright 官网)

本文为霍格沃兹测试开发学社学员学习笔记,人工智能测试开发进阶学习文末加群。一,Playwright简介Web自动化测试框架。跨平台多语言支持。支持Chromium、Firefox、WebKit...

为SpringDataJpa集成QueryObject模式

1.概览单表查询在业务开发中占比最大,是所有CRUDBoy的入门必备,所有人在JavaBean和SQL之间乐此不疲。而在我看来,该部分是最枯燥、最没有技术含量的“伪技能”。1.1.背景...

金字塔测试原理:写好单元测试的8个小技巧,一文总结

想必金字塔测试原理大家已经很熟悉了,近年来的测试驱动开放在各个公司开始盛行,测试代码先写的倡议被反复提及。鉴于此,许多中大型软件公司对单元测试的要求也逐渐提高。那么,编写单元测试有哪些小技巧可以借鉴和...

测试工程师通常用哪个单元测试库来测试Java程序?

测试工程师在测试Java程序时通常使用各种不同的单元测试库,具体选择取决于项目的需求和团队的偏好。我们先来看一些常用的Java单元测试库,以及它们的一些特点:  1.JUnit:  ·描述:JUn...

JAVA程序员自救之路——SpringAI评估

背景我们用SpringAI做了大模型的调用,RAG的实现。但是我们做的东西是否能满足我们业务的要求呢。比如我们问了一个复杂的问题,大模型能否快速准确的回答出来?是否会出现幻觉?这就需要我们构建一个完善...