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());@Autowiredprivate SrvSealUtil srvSealUtil;@PostConstructpublic 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;}}// 正確構建帶超時的連接URLprivate 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連接,會自動嘗試重連,并記錄宕機時長