SpringBoot实现系统监控:给应用装上“健康手环”

大家好,我是小悟。

一、系统监控:你的应用需要"体检医生"

想象一下:你的SpringBoot应用就像一个996的程序员,每天24小时不间断工作。某天它突然"猝死"了,而你却不知道它是何时倒下、为何倒下、倒下前有没有喊"救命"... 这就是没有监控的可怕之处!

系统监控就像是:

  • 健康手环:时刻监测应用的心跳(健康状态)
  • 行车记录仪:记录每一次接口调用的"交通事故"
  • 私人医生:自动诊断哪里出了问题,还能开药方(告警)
  • 行为分析师:分析用户的"奇怪"操作模式

二、监控大作战详细步骤

第1步:基础依赖 - 准备"体检工具箱"

xml 复制代码
<!-- 监控全家桶套餐 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

<!-- 健康检查加强版 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

<!-- 可视化监控面板 -->
<dependency>
    <groupId>de.codecentric</groupId>
    <artifactId>spring-boot-admin-starter-client</artifactId>
    <version>2.7.0</version>
</dependency>

<!-- 监控指标导出到Prometheus -->
<dependency>
    <groupId>io.micrometer</groupId>
    <artifactId>micrometer-registry-prometheus</artifactId>
</dependency>

第2步:配置监控权限 - 设置"诊室门禁"

yaml 复制代码
# application.yml
spring:
  application:
    name: my-application  # 给应用起个名字,方便认领

management:
  endpoints:
    web:
      exposure:
        include: "*"  # 开放所有监控端点
        exclude: env,beans  # 敏感信息
  
  endpoint:
    health:
      show-details: always  # 健康检查显示详细信息
      probes:
        enabled: true  # 开启K8s探针支持
    
    prometheus:
      enabled: true  # 开启Prometheus指标导出
  
  metrics:
    export:
      prometheus:
        enabled: true
    tags:
      application: ${spring.application.name}  # 给指标打标签
    
  info:
    env:
      enabled: true  # 显示环境信息

# 自定义健康检查项
info:
  app:
    name: "超级无敌SpringBoot应用"
    version: "v2.0.0"
    description: "这是一个被严密监控的应用,别想偷懒!"
    developer: "监控狂魔团队"
    motto: "没有监控的代码就是在裸奔"

第3步:自定义健康检查 - 编写"体检项目"

typescript 复制代码
import org.springframework.boot.actuate.health.Health;
import org.springframework.boot.actuate.health.HealthIndicator;
import org.springframework.stereotype.Component;
import java.util.Random;

/**
 * 数据库连接健康检查
 * 相当于检查应用的"消化系统"
 */
@Component("database")
public class DatabaseHealthIndicator implements HealthIndicator {
    
    private final Random random = new Random();
    
    @Override
    public Health health() {
        boolean isHealthy = checkDatabaseConnection();
        
        if (isHealthy) {
            return Health.up()
                .withDetail("message", "数据库连接正常,可以愉快地CRUD啦!")
                .withDetail("responseTime", "15ms")
                .withDetail("activeConnections", 25)
                .build();
        } else {
            return Health.down()
                .withDetail("message", "数据库连接失败,快去检查水管(连接)!")
                .withDetail("errorCode", "DB_CONN_001")
                .withDetail("suggestion", "试试重启大法?")
                .build();
        }
    }
    
    private boolean checkDatabaseConnection() {
        // 模拟检查,真实项目应该真的ping一下数据库
        return random.nextInt(100) > 10; // 90%的概率健康
    }
}

/**
 * 缓存健康检查
 * 相当于检查应用的"短期记忆"
 */
@Component("cache")
public class CacheHealthIndicator implements HealthIndicator {
    @Override
    public Health health() {
        return Health.up()
            .withDetail("status", "缓存运行正常")
            .withDetail("memoryUsage", "45%")
            .withDetail("hitRate", "89.7%")
            .withDetail("motto", "记不住的东西,交给缓存吧!")
            .build();
    }
}

/**
 * 外部API依赖检查
 * 相当于检查应用的"社交能力"
 */
@Component("externalApi")
public class ExternalApiHealthIndicator implements HealthIndicator {
    @Override
    public Health health() {
        // 模拟检查第三方API
        boolean wechatApi = checkWechatApi();
        boolean paymentApi = checkPaymentApi();
        
        if (wechatApi && paymentApi) {
            return Health.up()
                .withDetail("wechat", "微信API:连接正常,可以愉快聊天")
                .withDetail("payment", "支付API:钱包鼓鼓,随时收钱")
                .withDetail("overall", "所有外部API都很给力!")
                .build();
        } else {
            return Health.down()
                .withDetail("wechat", wechatApi ? "正常" : "失联中...")
                .withDetail("payment", paymentApi ? "正常" : "掉线了,没法收钱!")
                .withDetail("emergency", "快联系第三方客服!")
                .build();
        }
    }
    
    private boolean checkWechatApi() {
        return true; // 假装检查
    }
    
    private boolean checkPaymentApi() {
        return new Random().nextInt(100) > 5; // 95%可用率
    }
}

第4步:自定义监控指标 - 安装"行为记录仪"

arduino 复制代码
import io.micrometer.core.instrument.Counter;
import io.micrometer.core.instrument.MeterRegistry;
import io.micrometer.core.instrument.Timer;
import org.springframework.stereotype.Component;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * 业务指标监控
 * 相当于给应用安装"计步器"和"心率监测"
 */
@Component
public class BusinessMetrics {
    
    // 用户注册计数器(统计有多少人"入坑")
    private final Counter userRegistrationCounter;
    
    // 订单创建计数器(统计赚了多少钱)
    private final Counter orderCreationCounter;
    
    // 异常计数器(统计出了多少bug)
    private final Counter exceptionCounter;
    
    // 活跃用户数(看看多少人在"玩")
    private final AtomicInteger activeUsers;
    
    // API响应时间计时器(看看接口是不是"拖延症")
    private final Timer apiResponseTimer;
    
    // 自定义业务指标(比如:点赞数、评论数等)
    private final Counter likeCounter;
    
    public BusinessMetrics(MeterRegistry registry) {
        // 初始化所有指标
        userRegistrationCounter = Counter.builder("user.registration.total")
            .description("用户注册总数")
            .tag("application", "my-app")
            .register(registry);
            
        orderCreationCounter = Counter.builder("order.created.total")
            .description("订单创建总数")
            .tag("application", "my-app")
            .register(registry);
            
        exceptionCounter = Counter.builder("exception.total")
            .description("异常发生总数")
            .tag("application", "my-app")
            .register(registry);
            
        activeUsers = registry.gauge("user.active.current", 
            new AtomicInteger(0));
            
        apiResponseTimer = Timer.builder("api.response.time")
            .description("API响应时间")
            .publishPercentiles(0.5, 0.95, 0.99) // 统计50%, 95%, 99%分位
            .register(registry);
            
        likeCounter = Counter.builder("content.like.total")
            .description("内容点赞总数")
            .tag("application", "my-app")
            .register(registry);
    }
    
    // 用户注册时调用(又有人"入坑"啦!)
    public void incrementUserRegistration() {
        userRegistrationCounter.increment();
        System.out.println("恭喜!又有新用户注册啦!当前总数: " + 
            userRegistrationCounter.count());
    }
    
    // 订单创建时调用(钱钱钱!)
    public void incrementOrderCreation(double amount) {
        orderCreationCounter.increment();
        System.out.println("收到新订单!金额: " + amount + 
            ",订单总数: " + orderCreationCounter.count());
    }
    
    // 异常发生时调用(啊哦,出bug了)
    public void incrementException(String exceptionType) {
        exceptionCounter.increment();
        System.err.println("发现bug!类型: " + exceptionType + 
            ",bug总数: " + exceptionCounter.count());
    }
    
    // 更新活跃用户数(看看谁在摸鱼)
    public void setActiveUsers(int count) {
        activeUsers.set(count);
    }
    
    // 记录API响应时间(看看哪个接口在偷懒)
    public void recordApiTime(long millis, String apiName) {
        apiResponseTimer.record(millis, TimeUnit.MILLISECONDS);
        
        if (millis > 1000) {
            System.out.println("警告!接口 " + apiName + 
                " 响应缓慢,耗时: " + millis + "ms");
        }
    }
    
    // 点赞时调用(收到小心心)
    public void incrementLike() {
        likeCounter.increment();
        System.out.println("收到一个点赞!当前点赞数: " + likeCounter.count());
    }
}

第5步:AOP监控切面 - 安装"全方位摄像头"

java 复制代码
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

/**
 * 接口监控切面
 * 相当于在每个接口门口安装"摄像头+计时器"
 */
@Aspect
@Component
public class MonitorAspect {
    
    @Autowired
    private BusinessMetrics businessMetrics;
    
    // 监控所有Controller(重点监控对象)
    @Pointcut("execution(* com.example.controller..*.*(..))")
    public void controllerPointcut() {}
    
    // 监控Service层(业务逻辑层)
    @Pointcut("execution(* com.example.service..*.*(..))")
    public void servicePointcut() {}
    
    @Around("controllerPointcut()")
    public Object monitorController(ProceedingJoinPoint joinPoint) throws Throwable {
        long startTime = System.currentTimeMillis();
        String methodName = joinPoint.getSignature().toShortString();
        
        System.out.println("🎬 接口开始执行: " + methodName);
        
        try {
            Object result = joinPoint.proceed();
            long executionTime = System.currentTimeMillis() - startTime;
            
            // 记录响应时间
            businessMetrics.recordApiTime(executionTime, methodName);
            
            System.out.println("接口执行成功: " + methodName + 
                ",耗时: " + executionTime + "ms");
            
            return result;
            
        } catch (Exception e) {
            // 记录异常
            businessMetrics.incrementException(e.getClass().getSimpleName());
            
            System.err.println("接口执行失败: " + methodName + 
                ",异常: " + e.getMessage());
            
            throw e;
        }
    }
    
    @Around("servicePointcut()")
    public Object monitorService(ProceedingJoinPoint joinPoint) throws Throwable {
        String methodName = joinPoint.getSignature().toShortString();
        
        // 检查是不是重要业务方法
        if (methodName.contains("UserService.register")) {
            System.out.println("开始注册用户...");
        } else if (methodName.contains("OrderService.create")) {
            System.out.println("开始创建订单...");
        }
        
        try {
            return joinPoint.proceed();
        } catch (Exception e) {
            System.err.println("业务方法出错: " + methodName);
            businessMetrics.incrementException("ServiceException");
            throw e;
        }
    }
}

第6步:监控告警配置 - 设置"紧急呼叫按钮"

typescript 复制代码
import org.springframework.boot.actuate.health.Health;
import org.springframework.boot.actuate.health.HealthIndicator;
import org.springframework.mail.SimpleMailMessage;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import java.util.Date;

/**
 * 监控告警系统
 * 相当于应用的"私人医生紧急呼叫"
 */
@Component
public class MonitorAlert {
    
    @Autowired(required = false)
    private JavaMailSender mailSender;
    
    @Autowired
    private BusinessMetrics businessMetrics;
    
    private HealthIndicator databaseHealth;
    
    // 每小时检查一次健康状态(定期体检)
    @Scheduled(fixedRate = 60 * 60 * 1000)
    public void healthCheck() {
        System.out.println("开始定时健康检查...");
        
        // 检查异常数(看看是不是bug泛滥)
        double exceptionCount = businessMetrics.getExceptionCount();
        if (exceptionCount > 100) {
            sendAlert("异常数超标警告", 
                "系统异常数已达到 " + exceptionCount + ",快去修bug!");
        }
        
        // 检查响应时间(看看接口是不是在偷懒)
        double avgResponseTime = businessMetrics.getAvgResponseTime();
        if (avgResponseTime > 5000) {
            sendAlert("系统响应缓慢", 
                "平均响应时间 " + avgResponseTime + "ms,用户要跑光了!");
        }
        
        System.out.println("健康检查完成,一切正常!");
    }
    
    // 每天发送日报(每日病情报告)
    @Scheduled(cron = "0 0 18 * * ?") // 每天下午6点
    public void dailyReport() {
        String report = buildDailyReport();
        System.out.println("生成日报:\n" + report);
        
        if (mailSender != null) {
            sendEmail("系统监控日报 - " + new Date(), report);
        }
    }
    
    private String buildDailyReport() {
        return """
            ======== 系统监控日报 ========
            日期:%s
            今日新增用户:%d
            今日订单总数:%d
            今日点赞数:%d
            今日异常数:%d
            平均响应时间:%.2f ms
            系统健康状态:%s
            ============================
            """.formatted(
                new Date(),
                businessMetrics.getTodayUserRegistrations(),
                businessMetrics.getTodayOrders(),
                businessMetrics.getTodayLikes(),
                businessMetrics.getTodayExceptions(),
                businessMetrics.getAvgResponseTime(),
                getOverallHealthStatus()
            );
    }
    
    private void sendAlert(String title, String message) {
        System.err.println(title + ": " + message);
        
        // 发送邮件告警(如果配置了邮件)
        if (mailSender != null) {
            sendEmail("[紧急]" + title, message);
        }
        
        // 这里还可以集成钉钉、企业微信、短信等告警
        System.out.println("已发送告警通知!");
    }
    
    private void sendEmail(String subject, String text) {
        SimpleMailMessage message = new SimpleMailMessage();
        message.setTo("dev-team@example.com");
        message.setSubject(subject);
        message.setText(text);
        mailSender.send(message);
    }
    
    private String getOverallHealthStatus() {
        // 综合判断系统健康状况
        if (businessMetrics.getTodayExceptions() > 50) {
            return "生病了,需要紧急处理";
        } else if (businessMetrics.getAvgResponseTime() > 1000) {
            return "有点疲惫,需要优化";
        } else {
            return "非常健康,继续努力";
        }
    }
}

第7步:可视化监控面板 - 打造"监控指挥中心"

arduino 复制代码
import de.codecentric.boot.admin.server.config.EnableAdminServer;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

/**
 * Spring Boot Admin Server
 * 监控可视化面板,相当于"监控指挥中心大屏幕"
 */
@SpringBootApplication
@EnableAdminServer
public class MonitorDashboardApplication {
    public static void main(String[] args) {
        SpringApplication.run(MonitorDashboardApplication.class, args);
        System.out.println("""
            监控面板启动成功!
            访问地址: http://localhost:8080
            用户名: admin
            密码: admin
            
            在这里你可以看到:
            1. 所有服务的健康状态
            2. 实时性能指标
            3. 告警信息
            4. 日志查看
            5. 动态配置管理
            
            就像有了一个全天候的监控室!
            """);
    }
}

三、访问监控端点

应用启动后,可以访问以下监控端点:

bash 复制代码
健康检查:http://localhost:8080/actuator/health
所有指标:http://localhost:8080/actuator/metrics
Prometheus格式:http://localhost:8080/actuator/prometheus
应用信息:http://localhost:8080/actuator/info
环境变量:http://localhost:8080/actuator/env
Bean列表:http://localhost:8080/actuator/beans
映射关系:http://localhost:8080/actuator/mappings
配置属性:http://localhost:8080/actuator/configprops

四、完整配置示例

yaml 复制代码
# application-monitor.yml
spring:
  boot:
    admin:
      client:
        url: http://localhost:8080
        instance:
          name: ${spring.application.name}
          
  security:
    user:
      name: admin
      password: admin123
      
  mail:
    host: smtp.example.com
    port: 587
    username: monitor@example.com
    password: yourpassword
    properties:
      mail:
        smtp:
          auth: true
          starttls:
            enable: true

# 监控告警规则
monitor:
  alert:
    enabled: true
    email:
      recipients:
        - dev-team@example.com
        - ops-team@example.com
    thresholds:
      response-time: 5000  # 5秒
      error-rate: 0.05     # 5%
      memory-usage: 0.8    # 80%
    schedules:
      daily-report: "0 0 18 * * ?"  # 每天18点
      health-check: "0 */30 * * * ?" # 每30分钟

五、总结:监控让系统"透明化"

为什么要监控?(监控的五大好处)

  1. 预防胜于治疗
    • 在用户投诉前发现问题
    • 在系统崩溃前预警
    • 就像给系统打了"疫苗"
  2. 快速定位问题
    • 不再是"猜谜游戏"
    • 精准定位问题根因
    • 减少"重启试试"的次数
  3. 性能优化依据
    • 知道哪里慢,才知道怎么优化
    • 数据驱动的优化决策
    • 告别"我感觉这个接口有点慢"
  4. 容量规划参考
    • 知道系统能承受多少压力
    • 合理规划服务器资源
    • 避免"双十一"时系统崩溃
  5. 提升开发体验
    • 实时了解系统状态
    • 快速验证修复效果
    • 减少半夜被叫起来修bug的概率

监控的最佳实践

  1. 分层监控:从基础设施到应用层全链路监控
  2. 关键指标:监控黄金指标(延迟、流量、错误、饱和度)
  3. 智能告警:避免告警疲劳,设置合理的阈值
  4. 可视化:一图胜千言,好的图表让人秒懂状态
  5. 持续改进:根据监控数据不断优化系统

最后

没有监控的系统就像没有仪表的飞机------飞得高不高、快不快、安不安全,全凭感觉!等到坠机了,都不知道是没油了还是引擎坏了。

给你的SpringBoot应用装上监控,就像:

  • 给汽车装上了仪表盘+行车记录仪+胎压监测
  • 给程序员配上了智能手环+年度体检+健康教练
  • 给房子安装了烟雾报警器+摄像头+智能门锁

监控不是万能的,但没有监控是万万不能的!

现在,去给你的应用装上"健康手环",让它从此过上"透明、健康、可观测"的幸福生活吧!

温馨提示:监控虽好,也不要过度监控哦!毕竟,连上厕所都要统计时间和次数的话,系统也会感到"压力山大"的!

谢谢你看我的文章,既然看到这里了,如果觉得不错,随手点个赞、转发、在看三连吧,感谢感谢。那我们,下次再见。

您的一键三连,是我更新的最大动力,谢谢

山水有相逢,来日皆可期,谢谢阅读,我们再会

我手中的金箍棒,上能通天,下能探海

相关推荐
辜月十1 小时前
Docker-Compose 【Mysql】
后端
葡萄成熟时 !1 小时前
快捷键idea
java
GuluGuluWater1 小时前
受够了同事的垃圾代码?我用 Go 写了个自动审查工具
后端
楼田莉子1 小时前
Linux学习:基础IO相关学习
linux·开发语言·c++·后端·学习
吃喝不愁霸王餐APP开发者1 小时前
外卖霸王餐灰度开关:基于Spring Cloud Config+Bus动态刷新踩坑
java
雨中飘荡的记忆1 小时前
Spring Security详解
java·spring
golang学习记1 小时前
Go 中获取两个切片交集的 6 种方法
后端
Cache技术分享1 小时前
261. Java 集合 - Java 开发必备:ArrayList 与 LinkedList 的选择攻略
前端·后端
golang学习记1 小时前
换掉 Maven?全新一代 Java 项目构建工具来了 —— **Maven-mvnd**,性能炸裂!
后端