SpringBoot定时监控数据库状态

发布于:2025-06-20 ⋅ 阅读:(19) ⋅ 点赞:(0)

1.application.properties配置文件

# config for mysql
spring.datasource.url = jdbc\:mysql\://127.0.0.1\:3306/数据库名?characterEncoding\=utf8&useSSL\=false
spring.datasource.username = 账号
spring.datasource.password = 密码
spring.datasource.validation-query= SELECT 1
spring.jpa.properties.hibernate.dialect =org.hibernate.dialect.MySQL5Dialect
#spring.datasource.druid.driver-class-name=com.mysql.cj.jdbc.driver
# ================ 重连配置 ================
# 最大重试次数
spring.datasource.druid.connection-error-retry-attempts=10
# 重试间隔时间(毫秒)
spring.datasource.druid.time-between-connect-error-millis=10000
# 获取连接失败后中断
spring.datasource.druid.break-after-acquire-failure=false

# 自定义监控配置
monitoring.database.enabled=true
#30秒检查一次
monitoring.database.interval=30000
#5秒超时
monitoring.database.connection-timeout=5000

2.引入对应依赖

          <dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
			<version>8.0.33</version>
			<!-- 明确排除旧版依赖冲突 -->
			<exclusions>
				<exclusion>
					<groupId>mysql</groupId>
					<artifactId>mysql-connector-java</artifactId>
				</exclusion>
			</exclusions>
		</dependency>
         <!-- Druid 连接池 -->
		<dependency>
			<groupId>com.alibaba</groupId>
			<artifactId>druid-spring-boot-starter</artifactId>
			<version>1.2.16</version>
		</dependency>
<!-- Spring Actuator for Health Checks -->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-actuator</artifactId>
		</dependency>
<!-- 内置的有Schedul的依赖 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

3.代码实现

package com.dianju.signatureServer;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import srvSeal.SrvSealUtil;

import javax.annotation.PostConstruct;
import java.sql.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;

@RestController
@RequestMapping("/monitorservice")
public class MonitorController {
    private final Logger log = LoggerFactory.getLogger(this.getClass());
    @Value("${spring.datasource.url}")
    private String jdbcUrl;

    @Value("${spring.datasource.username}")
    private String username;

    @Value("${spring.datasource.password}")
    private String password;

    @Value("${spring.datasource.validation-query}")
    private String validationQuery;

    @Value("${monitoring.database.interval}")
    private long checkInterval;

    @Value("${monitoring.database.connection-timeout}")
    private int connectionTimeout;

    // 状态跟踪变量
    private final AtomicInteger consecutiveFailures = new AtomicInteger(0);
    private final AtomicLong lastFailureTime = new AtomicLong(0);
    private final AtomicLong lastSuccessTime = new AtomicLong(System.currentTimeMillis());

    @Autowired
    private SrvSealUtil srvSealUtil;

    @PostConstruct
    public void init() {
        try {
            // 显式加载MySQL驱动
            Class.forName("com.mysql.cj.jdbc.Driver");
            log.info("✅ MySQL驱动加载成功");
            // 打印配置信息
            log.info("数据库监控配置:");
            log.info("连接URL: {}", jdbcUrl);
            log.info("用户名: {}", username);
            log.info("验证查询: {}", validationQuery);
            log.info("监控间隔: {}ms", checkInterval);
            log.info("连接超时: {}ms", connectionTimeout);
        } catch (ClassNotFoundException e) {
            log.error("❌ 加载MySQL驱动失败", e);
            throw new RuntimeException("数据库驱动加载失败", e);
        }
    }

  
   @Scheduled(fixedRateString = "${monitoring.database.interval}")
    public void monitorDatabase() {
        long startTime = System.currentTimeMillis();
        boolean isHealthy = checkDatabaseHealth();
        long responseTime = System.currentTimeMillis() - startTime;

        if (isHealthy) {
            handleSuccess(responseTime);
        } else {
            handleFailure();
        }
    }

    private boolean checkDatabaseHealth() {
        try (Connection conn = DriverManager.getConnection(
                buildConnectionUrl(),
                username,
                password)) {

            try (Statement stmt = conn.createStatement();
                 ResultSet rs = stmt.executeQuery(validationQuery)) {

                return rs.next() && rs.getInt(1) == 1;
            }
        } catch (Exception e) {
            log.error("❌ 数据库健康检查失败", e);
            return false;
        }
    }

    // 正确构建带超时的连接URL
    private String buildConnectionUrl() {
        StringBuilder urlBuilder = new StringBuilder(jdbcUrl);

        if (jdbcUrl.contains("?")) {
            urlBuilder.append("&connectTimeout=").append(connectionTimeout);
        } else {
            urlBuilder.append("?connectTimeout=").append(connectionTimeout);
        }

        return urlBuilder.toString();
    }

    private void handleSuccess(long responseTime) {
        // 重置失败计数器
        consecutiveFailures.set(0);

        // 更新最后成功时间
        lastSuccessTime.set(System.currentTimeMillis());

        // 如果之前有失败记录,输出恢复信息
        if (lastFailureTime.get() > 0) {
            long downTime = System.currentTimeMillis() - lastFailureTime.get();
            log.info("\n🔥 数据库恢复可用! 宕机时长: {}秒\n", downTime / 1000);

            // 重置失败时间戳
            lastFailureTime.set(0);
        }

        // 输出正常日志
        log.info("✅ 数据库正常 | 响应时间: {}ms", responseTime);
    }

    private void handleFailure() {
        // 增加失败计数
        int failures = consecutiveFailures.incrementAndGet();

        // 记录第一次失败时间
        if (failures == 1) {
            lastFailureTime.set(System.currentTimeMillis());
        }

        // 获取宕机时长(秒)
        long downTime = 0;
        if (lastFailureTime.get() > 0) {
            downTime = (System.currentTimeMillis() - lastFailureTime.get()) / 1000;
        }

        // 输出错误日志
        log.error("❌ 数据库连接失败 | 连续失败次数: {} | 宕机时长: {}秒", failures, downTime);
    }

    // 获取数据库状态(供其他服务查询)
    public boolean isDatabaseUp() {
        return consecutiveFailures.get() == 0;
    }

    // 获取数据库最后健康时间
    public long getLastHealthyTime() {
        return lastSuccessTime.get();
    }

}

断开mysql连接,会自动尝试重连,并记录宕机时长


网站公告

今日签到

点亮在社区的每一天
去签到