本文目录导读:

我来提供一个完整的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两种主流连接池,包含实时监控、健康检查、告警和历史数据统计等功能。