Java数据库连接池监控案例

wen java案例 3

本文目录导读:

Java数据库连接池监控案例

  1. 项目结构
  2. Maven依赖配置
  3. 连接池配置
  4. 监控模型类
  5. 监控核心逻辑
  6. 定时监控调度器
  7. API控制器
  8. 服务层
  9. 配置文件
  10. 使用示例
  11. 监控效果展示

我来提供一个完整的Java数据库连接池监控案例,包括HikariCP、Druid两种主流连接池的监控实现。

项目结构

db-pool-monitor/
├── pom.xml
├── src/main/java/com/example/monitor/
│   ├── config/
│   │   ├── HikariCPConfig.java
│   │   ├── DruidConfig.java
│   │   └── DataSourceConfig.java
│   ├── monitor/
│   │   ├── ConnectionPoolMonitor.java
│   │   ├── MonitorScheduler.java
│   │   └── AlertService.java
│   ├── controller/
│   │   ├── PoolMonitorController.java
│   │   └── HealthCheckController.java
│   ├── service/
│   │   ├── DataSourceService.java
│   │   └── MetricsCollector.java
│   └── model/
│       ├── PoolMetrics.java
│       └── AlertMessage.java

Maven依赖配置

<dependencies>
    <!-- Spring Boot Web -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <!-- Spring Boot Actuator -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>
    <!-- HikariCP -->
    <dependency>
        <groupId>com.zaxxer</groupId>
        <artifactId>HikariCP</artifactId>
        <version>5.0.1</version>
    </dependency>
    <!-- Druid -->
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>druid-spring-boot-starter</artifactId>
        <version>1.2.20</version>
    </dependency>
    <!-- MySQL Driver -->
    <dependency>
        <groupId>com.mysql</groupId>
        <artifactId>mysql-connector-j</artifactId>
        <version>8.0.33</version>
    </dependency>
    <!-- Prometheus Metrics -->
    <dependency>
        <groupId>io.micrometer</groupId>
        <artifactId>micrometer-registry-prometheus</artifactId>
    </dependency>
    <!-- Lombok -->
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>
</dependencies>

连接池配置

HikariCP配置类

package com.example.monitor.config;
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
import io.micrometer.core.instrument.MeterRegistry;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jmx.export.MBeanExporter;
import org.springframework.jmx.support.RegistrationPolicy;
import javax.sql.DataSource;
import java.util.Properties;
@Configuration
public class HikariCPConfig {
    @Value("${spring.datasource.url}")
    private String jdbcUrl;
    @Value("${spring.datasource.username}")
    private String username;
    @Value("${spring.datasource.password}")
    private String password;
    @Value("${spring.datasource.hikari.maximum-pool-size:10}")
    private int maxPoolSize;
    @Bean
    public DataSource hikariDataSource(MeterRegistry meterRegistry) {
        HikariConfig config = new HikariConfig();
        // 数据库连接配置
        config.setJdbcUrl(jdbcUrl);
        config.setUsername(username);
        config.setPassword(password);
        config.setDriverClassName("com.mysql.cj.jdbc.Driver");
        // 连接池配置
        config.setPoolName("HikariPool-Main");
        config.setMaximumPoolSize(maxPoolSize);
        config.setMinimumIdle(5);
        config.setIdleTimeout(300000);          // 5分钟
        config.setConnectionTimeout(30000);     // 30秒
        config.setMaxLifetime(1800000);         // 30分钟
        config.setConnectionTestQuery("SELECT 1");
        // 监控配置
        config.setMetricRegistry(meterRegistry);
        config.setHealthCheckRegistry(new com.codahale.metrics.health.HealthCheckRegistry());
        // JMX监控
        Properties props = new Properties();
        props.setProperty("dataSource.cachePrepStmts", "true");
        props.setProperty("dataSource.prepStmtCacheSize", "250");
        props.setProperty("dataSource.prepStmtCacheSqlLimit", "2048");
        config.setDataSourceProperties(props);
        return new HikariDataSource(config);
    }
    @Bean
    public MBeanExporter exporter() {
        MBeanExporter exporter = new MBeanExporter();
        exporter.setRegistrationPolicy(RegistrationPolicy.IGNORE_EXISTING);
        return exporter;
    }
}

Druid配置类

package com.example.monitor.config;
import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.support.http.StatViewServlet;
import com.alibaba.druid.support.http.WebStatFilter;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.sql.DataSource;
import java.sql.SQLException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
@Configuration
public class DruidConfig {
    @Value("${spring.datasource.url}")
    private String url;
    @Value("${spring.datasource.username}")
    private String username;
    @Value("${spring.datasource.password}")
    private String password;
    @Bean
    public DataSource druidDataSource() throws SQLException {
        DruidDataSource dataSource = new DruidDataSource();
        // 基础连接信息
        dataSource.setUrl(url);
        dataSource.setUsername(username);
        dataSource.setPassword(password);
        dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
        // 连接池配置
        dataSource.setInitialSize(5);
        dataSource.setMinIdle(5);
        dataSource.setMaxActive(20);
        dataSource.setMaxWait(60000);
        dataSource.setTimeBetweenEvictionRunsMillis(60000);
        dataSource.setMinEvictableIdleTimeMillis(300000);
        dataSource.setValidationQuery("SELECT 1");
        dataSource.setTestWhileIdle(true);
        dataSource.setTestOnBorrow(false);
        dataSource.setTestOnReturn(false);
        // 监控配置
        dataSource.setFilters("stat,wall,log4j2");
        dataSource.setUseGlobalDataSourceStat(true);
        dataSource.setConnectionProperties("druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000");
        // 开启PSCache
        dataSource.setPoolPreparedStatements(true);
        dataSource.setMaxPoolPreparedStatementPerConnectionSize(20);
        return dataSource;
    }
    @Bean
    public ServletRegistrationBean<StatViewServlet> statViewServlet() {
        ServletRegistrationBean<StatViewServlet> reg = new ServletRegistrationBean<>(
            new StatViewServlet(), "/druid/*");
        // 设置监控页面访问权限
        reg.addInitParameter("loginUsername", "admin");
        reg.addInitParameter("loginPassword", "admin123");
        reg.addInitParameter("resetEnable", "false");
        reg.addInitParameter("allow", "127.0.0.1"); // IP白名单
        return reg;
    }
    @Bean
    public FilterRegistrationBean<WebStatFilter> webStatFilter() {
        FilterRegistrationBean<WebStatFilter> reg = new FilterRegistrationBean<>();
        reg.setFilter(new WebStatFilter());
        reg.setUrlPatterns(Arrays.asList("/*"));
        Map<String, String> initParams = new HashMap<>();
        initParams.put("exclusions", "*.js,*.css,/druid/*,/static/*");
        reg.setInitParameters(initParams);
        return reg;
    }
}

监控模型类

package com.example.monitor.model;
import lombok.Data;
import lombok.Builder;
import lombok.NoArgsConstructor;
import lombok.AllArgsConstructor;
import java.util.Map;
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class PoolMetrics {
    // 基础信息
    private String poolName;
    private String poolType;       // HIKARICP / DRUID
    private long timestamp;
    // 连接池状态
    private int activeConnections;
    private int idleConnections;
    private int totalConnections;
    private int maxConnections;
    private int minConnections;
    private int waitingThreads;
    // 连接池配置
    private long connectionTimeout;
    private long validationTimeout;
    private long maxLifetime;
    private long idleTimeout;
    // 性能指标
    private long totalConnectionsCreated;
    private long totalConnectionsClosed;
    private long totalConnectionsAcquired;
    private long totalConnectionsReleased;
    // 耗时统计
    private long avgConnectionTimeMs;
    private long maxConnectionTimeMs;
    private long connectionCreationTimeMs;
    // 错误统计
    private long connectionErrors;
    private long statementErrors;
    // 使用率
    private double usageRatio;
    private double waitRate;
    // Druid特有指标
    private Long activePeak;
    private Long activePeakTime;
    private Long poolingCount;
    private Long discardCount;
    private Long waitThreadCount;
    private Long waitCount;
    private Long notEmptyWaitCount;
    private Long notEmptyWaitNanos;
    public Map<String, Object> toMap() {
        return Map.of(
            "poolName", poolName,
            "poolType", poolType,
            "activeConnections", activeConnections,
            "idleConnections", idleConnections,
            "totalConnections", totalConnections,
            "maxConnections", maxConnections,
            "waitingThreads", waitingThreads,
            "usageRatio", usageRatio,
            "waitRate", waitRate
        );
    }
}

监控核心逻辑

package com.example.monitor.monitor;
import com.alibaba.druid.pool.DruidDataSource;
import com.example.monitor.model.PoolMetrics;
import com.zaxxer.hikari.HikariDataSource;
import com.zaxxer.hikari.metrics.PoolStats;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;
@Component
public class ConnectionPoolMonitor {
    private static final Logger logger = LoggerFactory.getLogger(ConnectionPoolMonitor.class);
    @Autowired
    private DataSource primaryDataSource;
    @Autowired(required = false)
    private HikariDataSource hikariDataSource;
    @Autowired(required = false)
    private DruidDataSource druidDataSource;
    /**
     * 收集HikariCP连接池指标
     */
    public PoolMetrics collectHikariMetrics() {
        if (hikariDataSource == null) {
            return null;
        }
        PoolStats poolStats = hikariDataSource.getHikariPoolMXBean() != null ? 
            hikariDataSource.getHikariPoolMXBean() : null;
        if (poolStats == null) {
            logger.warn("HikariCP pool stats not available");
            return null;
        }
        PoolStats stats = poolStats;
        int maxConnections = hikariDataSource.getMaximumPoolSize();
        int activeConnections = stats.getActiveConnections();
        int idleConnections = stats.getIdleConnections();
        return PoolMetrics.builder()
            .poolName("HikariPool-Main")
            .poolType("HIKARICP")
            .timestamp(System.currentTimeMillis())
            .activeConnections(activeConnections)
            .idleConnections(idleConnections)
            .totalConnections(activeConnections + idleConnections)
            .maxConnections(maxConnections)
            .waitingThreads(stats.getPendingThreads())
            .connectionTimeout(hikariDataSource.getConnectionTimeout())
            .maxLifetime(hikariDataSource.getMaxLifetime())
            .totalConnectionsCreated(stats.getTotalConnections())
            .totalConnectionsClosed(stats.getTotalConnections() - activeConnections - idleConnections)
            .usageRatio((double) activeConnections / maxConnections)
            .build();
    }
    /**
     * 收集Druid连接池指标
     */
    public PoolMetrics collectDruidMetrics() {
        if (druidDataSource == null) {
            return null;
        }
        try {
            return PoolMetrics.builder()
                .poolName("DruidPool-Main")
                .poolType("DRUID")
                .timestamp(System.currentTimeMillis())
                .activeConnections(druidDataSource.getActiveCount())
                .idleConnections(druidDataSource.getPoolingCount())
                .totalConnections(druidDataSource.getActiveCount() + druidDataSource.getPoolingCount())
                .maxConnections(druidDataSource.getMaxActive())
                .minConnections(druidDataSource.getMinIdle())
                .waitingThreads(druidDataSource.getWaitThreadCount())
                .activePeak(druidDataSource.getActivePeak())
                .activePeakTime(druidDataSource.getActivePeakTime())
                .poolingCount((long) druidDataSource.getPoolingCount())
                .discardCount(druidDataSource.getDiscardCount())
                .waitCount(druidDataSource.getWaitCount())
                .waitThreadCount((long) druidDataSource.getWaitThreadCount())
                .notEmptyWaitCount(druidDataSource.getNotEmptyWaitCount())
                .status(druidDataSource.isClosed() ? "closed" : "active")
                .usageRatio((double) druidDataSource.getActiveCount() / druidDataSource.getMaxActive())
                .build();
        } catch (Exception e) {
            logger.error("Failed to collect Druid metrics", e);
            return null;
        }
    }
    /**
     * 执行健康检查
     */
    public Map<String, Object> healthCheck() {
        Map<String, Object> result = new HashMap<>();
        // 检查HikariCP
        if (hikariDataSource != null) {
            Map<String, Object> hikariHealth = new HashMap<>();
            try {
                hikariHealth.put("status", "UP");
                hikariHealth.put("activeConnections", hikariDataSource.getHikariPoolMXBean().getActiveConnections());
                hikariHealth.put("idleConnections", hikariDataSource.getHikariPoolMXBean().getIdleConnections());
                // 测试连接
                long start = System.currentTimeMillis();
                try (var conn = hikariDataSource.getConnection()) {
                    hikariHealth.put("connectionTestTime", System.currentTimeMillis() - start - start);
                    hikariHealth.put("connectionValid", conn.isValid(5));
                }
            } catch (Exception e) {
                hikariHealth.put("status", "DOWN");
                hikariHealth.put("error", e.getMessage());
            }
            result.put("hikari", hikariHealth);
        }
        // 检查Druid
        if (druidDataSource != null) {
            Map<String, Object> druidHealth = new HashMap<>();
            try {
                druidHealth.put("status", "UP");
                druidHealth.put("activeConnections", druidDataSource.getActiveCount());
                druidHealth.put("poolingCount", druidDataSource.getPoolingCount());
                // 测试连接
                long start = System.currentTimeMillis();
                try (var conn = druidDataSource.getConnection()) {
                    druidHealth.put("connectionTestTime", System.currentTimeMillis() - start);
                    druidHealth.put("connectionValid", conn.isValid(5));
                }
            } catch (Exception e) {
                druidHealth.put("status", "DOWN");
                druidHealth.put("error", e.getMessage());
            }
            result.put("druid", druidHealth);
        }
        return result;
    }
    /**
     * 创建监控告警信息
     */
    static class AlertService {
        private static final Logger logger = LoggerFactory.getLogger(AlertService.class);
        public void alert(PoolMetrics metrics) {
            // 连接池使用率告警
            if (metrics.getUsageRatio() > 0.8) {
                logger.error("连接池使用率过高!Pool: {}, Usage: {}%", 
                    metrics.getPoolName(), 
                    String.format("%.2f", metrics.getUsageRatio() * 100));
            }
            // 等待线程数告警
            if (metrics.getWaitingThreads() > 10) {
                logger.error("等待线程数过多!Pool: {}, Waiting: {}", 
                    metrics.getPoolName(), 
                    metrics.getWaitingThreads());
            }
            // 活跃连接接近上限
            if (metrics.getActiveConnections() >= metrics.getMaxConnections() * 0.9) {
                logger.warn("活跃连接数接近上限!Pool: {}, Active: {}, Max: {}", 
                    metrics.getPoolName(),
                    metrics.getActiveConnections(), 
                    metrics.getMaxConnections());
            }
        }
    }
}

定时监控调度器

package com.example.monitor.monitor;
import com.example.monitor.model.PoolMetrics;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import java.util.concurrent.ConcurrentLinkedQueue;
@Component
@EnableScheduling
public class MonitorScheduler {
    private static final Logger logger = LoggerFactory.getLogger(MonitorScheduler.class);
    @Autowired
    private ConnectionPoolMonitor poolMonitor;
    @Autowired
    private AlertService alertService;
    private final ConcurrentLinkedQueue<PoolMetrics> metricsHistory = new ConcurrentLinkedQueue<>();
    private static final int MAX_HISTORY_SIZE = 1000;
    /**
     * 定时采集连接池指标(每10秒)
     */
    @Scheduled(fixedDelay = 10000)
    public void collectMetrics() {
        logger.debug("开始采集连接池指标...");
        PoolMetrics hikariMetrics = poolMonitor.collectHikariMetrics();
        if (hikariMetrics != null) {
            addToHistory(hikariMetrics);
            alertService.alert(hikariMetrics);
            logMetrics(hikariMetrics);
        }
        PoolMetrics druidMetrics = poolMonitor.collectDruidMetrics();
        if (druidMetrics != null) {
            addToHistory(druidMetrics);
            alertService.alert(druidMetrics);
            logMetrics(druidMetrics);
        }
    }
    /**
     * 每小时清理历史数据
     */
    @Scheduled(cron = "0 0 * * * ?")
    public void cleanupHistory() {
        long cutoffTime = System.currentTimeMillis() - 24 * 60 * 60 * 1000; // 保留24小时
        metricsHistory.removeIf(metrics -> metrics.getTimestamp() < cutoffTime);
        logger.info("清理完成,当前历史数据数量: {}", metricsHistory.size());
    }
    private void addToHistory(PoolMetrics metrics) {
        metricsHistory.offer(metrics);
        if (metricsHistory.size() > MAX_HISTORY_SIZE) {
            metricsHistory.poll();
        }
    }
    private void logMetrics(PoolMetrics metrics) {
        logger.info("连接池监控 - Pool: {}, Type: {}, Active: {}, Idle: {}, Total: {}, Max: {}, Waiting: {}", 
            metrics.getPoolName(),
            metrics.getPoolType(),
            metrics.getActiveConnections(),
            metrics.getIdleConnections(),
            metrics.getTotalConnections(),
            metrics.getMaxConnections(),
            metrics.getWaitingThreads());
    }
    public ConcurrentLinkedQueue<PoolMetrics> getMetricsHistory() {
        return metricsHistory;
    }
    public static class AlertService {
        public void alert(PoolMetrics metrics) {
            // 告警逻辑
            if (metrics.getUsageRatio() > 0.8) {
                logger.error("Connection pool usage ratio high: {}%", 
                    String.format("%.2f", metrics.getUsageRatio() * 100));
            }
        }
    }
}

API控制器

package com.example.monitor.controller;
import com.example.monitor.model.PoolMetrics;
import com.example.monitor.monitor.ConnectionPoolMonitor;
import com.example.monitor.monitor.MonitorScheduler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
@RestController
@RequestMapping("/api/monitor")
public class PoolMonitorController {
    @Autowired
    private ConnectionPoolMonitor poolMonitor;
    @Autowired
    private MonitorScheduler monitorScheduler;
    /**
     * 获取连接池实时指标
     */
    @GetMapping("/metrics")
    public ResponseEntity<Map<String, Object>> getMetrics() {
        Map<String, Object> result = new HashMap<>();
        PoolMetrics hikariMetrics = poolMonitor.collectHikariMetrics();
        PoolMetrics druidMetrics = poolMonitor.collectDruidMetrics();
        result.put("hikari", hikariMetrics);
        result.put("druid", druidMetrics);
        result.put("timestamp", System.currentTimeMillis());
        return ResponseEntity.ok(result);
    }
    /**
     * 获取连接池健康检查状态
     */
    @GetMapping("/health")
    public ResponseEntity<Map<String, Object>> getHealth() {
        return ResponseEntity.ok(poolMonitor.healthCheck());
    }
    /**
     * 获取历史监控数据
     */
    @GetMapping("/history")
    public ResponseEntity<Map<String, Object>> getHistory(
            @RequestParam(defaultValue = "10") int limit) {
        Map<String, Object> result = new HashMap<>();
        List<PoolMetrics> hikariHistory = monitorScheduler.getMetricsHistory()
            .stream()
            .filter(m -> "HIKARICP".equals(m.getPoolType()))
            .limit(limit)
            .collect(Collectors.toList());
        List<PoolMetrics> druidHistory = monitorScheduler.getMetricsHistory()
            .stream()
            .filter(m -> "DRUID".equals(m.getPoolType()))
            .limit(limit)
            .collect(Collectors.toList());
        result.put("hikari_history", hikariHistory);
        result.put("druid_history", druidHistory);
        result.put("total_records", monitorScheduler.getMetricsHistory().size());
        return ResponseEntity.ok(result);
    }
    /**
     * 连接池压力测试接口
     */
    @PostMapping("/stress-test")
    public ResponseEntity<Map<String, Object>> stressTest(
            @RequestParam(defaultValue = "100") int iterations,
            @RequestParam(defaultValue = "100") int delayMs) {
        Map<String, Object> result = new HashMap<>();
        List<Long> connectionTimes = new java.util.ArrayList<>();
        for (int i = 0; i < iterations; i++) {
            long start = System.nanoTime();
            // 获取连接并执行简单查询
            try (var conn = poolMonitor.getDataSource().getConnection();
                 var stmt = conn.createStatement();
                 var rs = stmt.executeQuery("SELECT 1")) {
                if (rs.next()) {
                    long duration = (System.nanoTime() - start) / 1000; // 微秒
                    connectionTimes.add(duration);
                }
            } catch (Exception e) {
                result.put("error", e.getMessage());
                break;
            }
            try {
                Thread.sleep(delayMs);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                break;
            }
        }
        // 分析结果
        double avgTime = connectionTimes.stream()
            .mapToLong(Long::longValue)
            .average()
            .orElse(0);
        long maxTime = connectionTimes.stream()
            .mapToLong(Long::longValue)
            .max()
            .orElse(0);
        long minTime = connectionTimes.stream()
            .mapToLong(Long::longValue)
            .min()
            .orElse(0);
        result.put("success_count", connectionTimes.size());
        result.put("avg_time_us", avgTime);
        result.put("max_time_us", maxTime);
        result.put("min_time_us", minTime);
        result.put("pool_status", poolMonitor.collectHikariMetrics());
        return ResponseEntity.ok(result);
    }
    /**
     * Prometheus格式的监控指标
     */
    @GetMapping("/metrics/prometheus")
    public ResponseEntity<String> getPrometheusMetrics() {
        StringBuilder sb = new StringBuilder();
        PoolMetrics metrics = poolMonitor.collectHikariMetrics();
        if (metrics != null) {
            sb.append("# HELP pool_connection_active Active connections\n");
            sb.append("# TYPE pool_connection_active gauge\n");
            sb.append("pool_connection_active ").append(metrics.getActiveConnections()).append("\n");
            sb.append("# HELP pool_connection_idle Idle connections\n");
            sb.append("# TYPE pool_connection_idle gauge\n");
            sb.append("pool_connection_idle ").append(metrics.getIdleConnections()).append("\n");
            sb.append("# HELP pool_connection_usage_ratio Connection pool usage ratio\n");
            sb.append("# TYPE pool_connection_usage_ratio gauge\n");
            sb.append("pool_connection_usage_ratio ").append(metrics.getUsageRatio()).append("\n");
            sb.append("# HELP pool_connection_waiting_threads Waiting threads\n");
            sb.append("# TYPE pool_connection_waiting_threads gauge\n");
            sb.append("pool_connection_waiting_threads ").append(metrics.getWaitingThreads()).append("\n");
        }
        return ResponseEntity.ok(sb.toString());
    }
}

服务层

package com.example.monitor.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.Statement;
@Service
public class DataSourceService {
    @Autowired
    private DataSource dataSource;
    /**
     * 测试数据库连接
     */
    public boolean testConnection() {
        try (Connection conn = dataSource.getConnection()) {
            try (Statement stmt = conn.createStatement()) {
                try (ResultSet rs = stmt.executeQuery("SELECT 1")) {
                    return rs.next() && rs.getInt(1) == 1;
                }
            }
        } catch (Exception e) {
            return false;
        }
    }
    /**
     * 获取数据库元数据
     */
    public Map<String, Object> getDatabaseInfo() {
        Map<String, Object> info = new HashMap<>();
        try (Connection conn = dataSource.getConnection()) {
            var metaData = conn.getMetaData();
            info.put("url", metaData.getURL());
            info.put("username", metaData.getUserName());
            info.put("driverName", metaData.getDriverName());
            info.put("driverVersion", metaData.getDriverVersion());
            info.put("databaseProductName", metaData.getDatabaseProductName());
            info.put("databaseProductVersion", metaData.getDatabaseProductVersion());
        } catch (Exception e) {
            info.put("error", e.getMessage());
        }
        return info;
    }
}

配置文件

# application.yml
spring:
  application:
    name: db-pool-monitor
  datasource:
    url: jdbc:mysql://localhost:3306/test?useSSL=false&serverTimezone=UTC&characterEncoding=utf8mb4
    username: root
    password: password
    # HikariCP配置
    hikari:
      pool-name: HikariPool-Main
      minimum-idle: 5
      maximum-pool-size: 20
      idle-timeout: 300000
      max-lifetime: 1800000
      connection-timeout: 30000
      connection-test-query: SELECT 1
    # Druid配置
    druid:
      initial-size: 5
      min-idle: 5
      max-active: 20
      max-wait: 60000
      time-between-eviction-runs-millis: 60000
      min-evictable-idle-time-millis: 300000
      validation-query: SELECT 1
      test-while-idle: true
      test-on-borrow: false
      test-on-return: false
      stat-view-servlet:
        enabled: true
        url-pattern: /druid/*
        login-username: admin
        login-password: admin123
      web-stat-filter:
        enabled: true
        exclusions: "*.js,*.css,/druid/*"
management:
  endpoints:
    web:
      exposure:
        include: health,info,metrics,prometheus
  metrics:
    export:
      prometheus:
        enabled: true
logging:
  level:
    com.example.monitor: DEBUG
    com.zaxxer.hikari: INFO
    com.alibaba.druid: INFO

使用示例

package com.example.monitor.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/demo")
public class DemoController {
    @GetMapping("/test")
    public String testDB() {
        // 模拟数据库操作
        for (int i = 0; i < 10; i++) {
            try (var conn = dataSource.getConnection()) {
                try (var stmt = conn.createStatement()) {
                    try (var rs = stmt.executeQuery("SELECT NOW()")) {
                        if (rs.next()) {
                            System.out.println("Query result: " + rs.getString(1));
                        }
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return "DB test completed";
    }
}

监控效果展示

监控面板访问地址:

  • HikariCP指标: http://localhost:8080/api/monitor/metrics
  • Druid监控页面: http://localhost:8080/druid/
  • 健康检查: http://localhost:8080/api/monitor/health
  • Prometheus指标: http://localhost:8080/api/monitor/metrics/prometheus
  • 历史数据: http://localhost:8080/api/monitor/history

配置告警规则(监控阈值):

  • 连接池使用率 > 80%
  • 等待线程数 > 10
  • 连接获取超时
  • 连接创建失败

这个案例提供了完整的数据库连接池监控解决方案,支持HikariCP和Druid两种主流连接池,包含实时监控、健康检查、告警和历史数据统计等功能。

抱歉,评论功能暂时关闭!