java

关注公众号 jb51net

关闭
首页 > 软件编程 > java > SpringBoot健康检查

SpringBoot实现健康检查的完整指南

作者:野生技术架构师

本文介绍了Spring Boot实现健康检查的方法,包括使用Actuator进行应用健康检查,自定义健康检查项目,搭建可视化监控大屏和配置告警系统,此外,还提供了实战案例和避坑指南,帮助读者更好地理解和应用Spring Boot健康检查功能,需要的朋友可以参考下
每天5分钟,掌握一个 SpringBoot核心知识点。大家好,我是SpringBoot指南的小坏。前两天我们聊了日志和监控,今天来点更实际的——如果你的应用”病”了,你怎么知道?知道了又该怎么办?

一、你的应用可能正在”带病工作”

先来看几个真实场景:

场景1
用户:”为什么我支付成功了,订单还是没生成?”
你:”数据库有点慢,重启一下就好了。”
真相:数据库连接池泄漏,已经持续3天了。

场景2
老板:”最近网站怎么这么卡?”
你:”服务器配置不够,加几台机器吧。”
真相:一个SQL查询没加索引,全表扫描拖慢了整个系统。

场景3
运维:”内存快爆了!”
你:”Java应用就是这样,重启一下就好了。”
真相:内存泄漏,每天泄漏50MB,一个月后必崩。

这些问题的共同点:应用在”带病工作”,但没人知道它”病”在哪。直到用户投诉、老板发火、服务器宕机…

二、SpringBoot的”体检中心”——Actuator

SpringBoot自带了一个”体检中心”,叫Actuator。你可以理解为:

2.1 3步开启体检中心

第一步:加个”体检设备”(依赖)

<!-- 在pom.xml里加这一行 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

第二步:打开”体检开关”(配置)

# application.yml
management:
  endpoints:
    web:
      exposure:
        include: "*"  # 打开所有体检项目

第三步:看看”体检报告” 启动应用,访问:http://localhost:8080/actuator/health

你会看到:

{
  "status": "UP",  // 健康状态:UP健康,DOWN生病
  "components": {
    "diskSpace": {
      "status": "UP",
      "details": {
        "total": "500GB",   // 磁盘总量
        "free": "300GB",    // 剩余空间
        "threshold": "10GB" // 警戒线
      }
    },
    "ping": {
      "status": "UP"  // 基本心跳
    }
  }
}

2.2 最重要的4个体检项目

  1. /actuator/health - 总体健康
    就像量血压、测心跳 告诉你:应用还活着吗?
  2. /actuator/info - 基本信息
    就像身份证 告诉你:这是谁?什么版本?什么时候出生的?
  3. /actuator/metrics - 性能指标
    就像全面体检 告诉你:CPU高不高?内存够不够?请求多不多?
  4. /actuator/loggers - 日志管理
    就像病历本 告诉你:现在记录了什么?还能改记录级别

三、自定义体检项目:检查第三方服务

光检查自己健康还不够,还要检查你依赖的”朋友”(其他服务)是否健康。

3.1 检查数据库连接

@Component
public class DatabaseHealthCheck {
    
    @Autowired
    private DataSource dataSource;
    
    // 这个方法会出现在/actuator/health里
    public Health check() {
        try {
            // 尝试获取数据库连接
            Connection conn = dataSource.getConnection();
            
            // 检查连接是否有效
            if (conn.isValid(2)) { // 2秒超时
                return Health.up()
                    .withDetail("message", "数据库连接正常")
                    .withDetail("time", LocalDateTime.now())
                    .build();
            } else {
                return Health.down()
                    .withDetail("error", "数据库连接无效")
                    .build();
            }
        } catch (Exception e) {
            return Health.down()
                .withDetail("error", "数据库连接失败")
                .withDetail("reason", e.getMessage())
                .build();
        }
    }
}

访问/actuator/health,你会看到:

{
  "status": "DOWN",  // 总体不健康!
  "components": {
    "db": {
      "status": "DOWN",
      "details": {
        "error": "数据库连接失败",
        "reason": "Connection refused"
      }
    }
  }
}

3.2 检查Redis是否正常

@Component
public class RedisHealthCheck {
    
    @Autowired
    private RedisTemplate<String, String> redisTemplate;
    
    public Health check() {
        try {
            // 执行一个简单的PING命令
            String result = redisTemplate.execute(
                (RedisCallback<String>) connection -> 
                    connection.ping()
            );
            
            if ("PONG".equals(result)) {
                return Health.up()
                    .withDetail("message", "Redis服务正常")
                    .build();
            } else {
                return Health.down()
                    .withDetail("error", "Redis返回异常")
                    .build();
            }
        } catch (Exception e) {
            return Health.down()
                .withDetail("error", "Redis连接失败")
                .withDetail("reason", e.getMessage())
                .build();
        }
    }
}

3.3 检查第三方API

@Component
public class ThirdPartyHealthCheck {
    
    @Autowired
    private RestTemplate restTemplate;
    
    public Health check() {
        // 检查支付接口
        Health paymentHealth = checkPaymentService();
        
        // 检查短信接口
        Health smsHealth = checkSmsService();
        
        // 如果有一个不健康,总体就不健康
        if (paymentHealth.getStatus() == Status.DOWN || 
            smsHealth.getStatus() == Status.DOWN) {
            return Health.down()
                .withDetail("payment", paymentHealth)
                .withDetail("sms", smsHealth)
                .build();
        }
        
        return Health.up()
            .withDetail("payment", paymentHealth)
            .withDetail("sms", smsHealth)
            .build();
    }
    
    private Health checkPaymentService() {
        try {
            ResponseEntity<String> response = restTemplate.getForEntity(
                "https://payment.api.com/health", 
                String.class
            );
            
            if (response.getStatusCode().is2xxSuccessful()) {
                return Health.up().build();
            } else {
                return Health.down()
                    .withDetail("status", response.getStatusCodeValue())
                    .build();
            }
        } catch (Exception e) {
            return Health.down()
                .withDetail("error", e.getMessage())
                .build();
        }
    }
}

四、可视化监控大屏:Grafana

看JSON太累?我们需要一个更直观的”体检报告大屏”。

4.1 什么是Grafana?

简单说,Grafana就是:

4.2 5分钟搭建监控大屏

第一步:加个”数据采集器”

<dependency>
    <groupId>io.micrometer</groupId>
    <artifactId>micrometer-registry-prometheus</artifactId>
</dependency>

第二步:暴露数据接口

management:
  endpoints:
    web:
      exposure:
        include: prometheus,health,metrics

第三步:启动Grafana(Docker最简单)

# 创建一个docker-compose.yml文件
version: '3'
services:
  prometheus:
    image: prom/prometheus
    ports:
      - "9090:9090"
      
  grafana:
    image: grafana/grafana
    ports:
      - "3000:3000"
    environment:
      - GF_SECURITY_ADMIN_PASSWORD=admin

# 运行
docker-compose up -d

第四步:访问大屏

  1. 打开 http://localhost:3000
  2. 用户名:admin,密码:admin
  3. 导入SpringBoot监控模板

你就能看到这样的酷炫大屏:

┌─────────────────────────────────────┐
│  SpringBoot应用监控                │
├─────────────────────────────────────┤
│  ✅ CPU使用率:25%                │
│  ✅ 内存使用:1.2G/2G             │
│  ✅ 请求量:1200次/分钟           │
│  ❌ 错误率:3.2% (偏高)           │
│  ✅ 数据库连接:45/100            │
│  ✅ 响应时间:平均125ms           │
└─────────────────────────────────────┘

4.3 最重要的5个监控图表

  1. 请求量折线图
  1. 错误率饼图
  1. 响应时间趋势图
  1. JVM内存堆栈图
  1. 数据库连接池图

五、告警:让系统自己”喊救命”

监控有了,但总不能24小时盯着屏幕吧?你需要告警——让系统自己”喊救命”。

5.1 配置钉钉告警(最常用)

第一步:创建钉钉机器人

  1. 钉钉群 → 群设置 → 智能群助手 → 添加机器人
  2. 选择”自定义”
  3. 设置机器人名字,比如”系统监控机器人”
  4. 复制webhook地址

第二步:配置告警规则

# application.yml
management:
  health:
    # 设置健康检查的细节显示
    show-details: always
    
  # 钉钉告警配置
  endpoint:
    health:
      enabled: true
      
alert:
  dingtalk:
    webhook: https://oapi.dingtalk.com/robot/send?access_token=你的token

第三步:写告警代码

@Component
public class HealthAlert {
    
    @Autowired
    private DingTalkService dingTalk;
    
    // 监听健康状态变化
    @EventListener
    public void onHealthChanged(Health health) {
        if (health.getStatus() == Status.DOWN) {
            // 发送钉钉告警
            String message = String.format(
                "【系统告警】\n" +
                "应用状态:不健康\n" +
                "时间:%s\n" +
                "详情:%s",
                LocalDateTime.now(),
                health.getDetails()
            );
            
            dingTalk.sendAlert(message);
        }
    }
}

5.2 告警消息示例

普通告警(发到钉钉群):

【系统监控】用户服务响应时间偏高
服务:user-service
实例:192.168.1.100:8080
当前响应时间:2.1s
阈值:1.0s
时间:2024-01-15 14:30:00
建议:检查数据库索引

紧急告警(打电话):

【紧急告警】订单服务数据库连接失败!
状态:DOWN
问题:数据库连接池耗尽
影响:用户无法下单
时间:2024-01-15 14:35:00
操作:请立即重启或扩容!

5.3 告警分级策略

alert:
  levels:
    P0:  # 最高级:必须马上处理
      conditions:
        - 服务完全不可用
        - 数据库连接失败
        - 核心业务失败率>20%
      actions:
        - 打电话
        - 发钉钉
        - 发短信
        
    P1:  # 高级:1小时内处理
      conditions:
        - 响应时间>3s
        - 错误率>10%
        - 磁盘使用率>90%
      actions:
        - 发钉钉
        - 发邮件
        
    P2:  # 中级:今天处理
      conditions:
        - 内存使用率>80%
        - CPU使用率>70%
        - 请求量突增200%
      actions:
        - 发邮件
        - 记录日志
        
    P3:  # 低级:本周优化
      conditions:
        - 慢查询数量增加
        - 日志错误率<1%
        - 缓存命中率下降
      actions:
        - 记录日志
        - 周会讨论

六、实战案例:电商系统健康检查

假设你有一个电商系统,需要检查这些:

@Component
public class EcommerceHealthCheck {
    
    @Autowired
    private OrderService orderService;
    
    @Autowired
    private PaymentService paymentService;
    
    @Autowired
    private InventoryService inventoryService;
    
    @Autowired
    private RedisTemplate<String, String> redisTemplate;
    
    public Health check() {
        Map<String, Health> details = new HashMap<>();
        
        // 1. 检查订单服务
        details.put("orderService", checkOrderService());
        
        // 2. 检查支付服务
        details.put("paymentService", checkPaymentService());
        
        // 3. 检查库存服务
        details.put("inventoryService", checkInventoryService());
        
        // 4. 检查缓存
        details.put("redis", checkRedis());
        
        // 5. 检查数据库
        details.put("database", checkDatabase());
        
        // 判断整体健康状态
        boolean allUp = details.values().stream()
            .allMatch(h -> h.getStatus() == Status.UP);
        
        if (allUp) {
            return Health.up()
                .withDetails(details)
                .build();
        } else {
            return Health.down()
                .withDetails(details)
                .build();
        }
    }
    
    private Health checkOrderService() {
        try {
            // 模拟创建订单
            Order testOrder = orderService.createTestOrder();
            
            return Health.up()
                .withDetail("message", "订单服务正常")
                .withDetail("testOrderId", testOrder.getId())
                .build();
        } catch (Exception e) {
            return Health.down()
                .withDetail("error", "订单服务异常")
                .withDetail("reason", e.getMessage())
                .build();
        }
    }
    
    private Health checkRedis() {
        try {
            // 测试Redis连接和性能
            long start = System.currentTimeMillis();
            redisTemplate.opsForValue().set("health_check", "test");
            String value = redisTemplate.opsForValue().get("health_check");
            long cost = System.currentTimeMillis() - start;
            
            if ("test".equals(value)) {
                return Health.up()
                    .withDetail("message", "Redis正常")
                    .withDetail("responseTime", cost + "ms")
                    .build();
            } else {
                return Health.down()
                    .withDetail("error", "Redis数据不一致")
                    .build();
            }
        } catch (Exception e) {
            return Health.down()
                .withDetail("error", "Redis连接失败")
                .build();
        }
    }
}

访问/actuator/health,你会看到:

{
  "status": "UP",
  "components": {
    "orderService": {
      "status": "UP",
      "details": {
        "message": "订单服务正常",
        "testOrderId": "123456"
      }
    },
    "paymentService": {
      "status": "UP",
      "details": {
        "message": "支付服务正常",
        "responseTime": "45ms"
      }
    },
    "redis": {
      "status": "DOWN",
      "details": {
        "error": "Redis连接失败",
        "reason": "Connection refused"
      }
    }
  }
}

关键信息

七、避坑指南

坑1:健康检查本身把系统搞挂了

// ❌ 错误:健康检查太耗时
public Health check() {
    // 执行一个10秒的SQL查询...
    ResultSet rs = executeLongQuery();
    return Health.up().build();
}

// ✅ 正确:设置超时时间
public Health check() {
    try {
        Future<Boolean> future = executor.submit(() -> {
            return checkDatabase();
        });
        
        // 最多等2秒
        boolean healthy = future.get(2, TimeUnit.SECONDS);
        return healthy ? Health.up() : Health.down();
    } catch (TimeoutException e) {
        return Health.down()
            .withDetail("error", "健康检查超时")
            .build();
    }
}

坑2:敏感信息泄露

// ❌ 错误:暴露了数据库密码
return Health.up()
    .withDetail("database", "连接正常")
    .withDetail("url", "jdbc:mysql://localhost:3306")
    .withDetail("username", "root")
    .withDetail("password", "123456")  // 天啊!密码泄露了!
    .build();

// ✅ 正确:只暴露必要信息
return Health.up()
    .withDetail("database", "连接正常")
    .withDetail("响应时间", "20ms")
    .build();

坑3:告警太多,变成”狼来了”

# ❌ 错误:什么都告警
alert:
  rules:
    - CPU使用率 > 50%  # 太敏感了!
    - 内存使用率 > 60%
    - 请求量增加 10%
    - 响应时间 > 100ms

# ✅ 正确:只告警关键问题
alert:
  rules:
    - 服务不可用
    - 错误率 > 5%
    - 响应时间 > 3s
    - 磁盘使用率 > 90%

八、最佳实践总结

8.1 健康检查配置清单

# 必须检查的项目
health:
  checks:
    # 系统层面
    - 磁盘空间
    - 内存使用
    - CPU负载
    
    # 应用层面
    - 数据库连接
    - Redis连接
    - 消息队列
    
    # 业务层面
    - 核心API可用性
    - 第三方服务
    - 定时任务状态

8.2 监控告警检查清单

8.3 一个完整的配置示例

# application-prod.yml
management:
  endpoints:
    web:
      exposure:
        include: health,info,metrics,prometheus
      base-path: /monitor  # 自定义路径,更安全
      
  endpoint:
    health:
      show-details: when-authorized  # 只有授权用户能看到详情
      probes:
        enabled: true  # 开启就绪和存活检查
      
  # 安全配置
  security:
    enabled: true
    roles: ADMIN  # 需要ADMIN角色
    
  # 指标配置
  metrics:
    export:
      prometheus:
        enabled: true
    tags:
      application: ${spring.application.name}
      environment: prod
      
# 自定义健康检查
custom:
  health:
    # 检查频率
    check-interval: 30s
    # 超时时间
    timeout: 5s
    # 重试次数
    retry-times: 3

九、今日思考题

场景:你是公司的技术负责人,需要设计一套健康检查方案:

  1. 给老板看什么?
  1. 给运维看什么?
  1. 给开发看什么?

以上就是SpringBoot实现健康检查的完整指南的详细内容,更多关于SpringBoot健康检查的资料请关注脚本之家其它相关文章!

您可能感兴趣的文章:
阅读全文