先得配置 spring.datasource.hikari.is-register-mbeans: true 才能在代码中正确获取连接池的统计信息。

@PostConstruct
public void monitor() {
    ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();
    executor.scheduleAtFixedRate(() -> {
        HikariPoolMXBean hikariPoolMXBean = dataSource.getHikariPoolMXBean();
        if (null != hikariPoolMXBean) {
            log.info("total:{}, active:{}, idle:{}, awaiting:{}", 
            		hikariPoolMXBean.getTotalConnections(),
                    hikariPoolMXBean.getActiveConnections(),
                    hikariPoolMXBean.getIdleConnections(),
                    hikariPoolMXBean.getThreadsAwaitingConnection());
        }
    }, 0, 100, TimeUnit.MILLISECONDS);
}

这段代码定义了一个名为 monitor 的方法,并使用了 @PostConstruct 注解,这意味着该方法将在Spring容器完成bean的实例化、属性填充和依赖注入后立即执行一次。具体来说,这段代码实现了对HikariCP连接池状态的定期监控,并将信息打印到日志中。

代码解析

  1. ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();
  • 创建一个新的单线程调度执行器 (ScheduledExecutorService)。这个执行器用于安排以固定速率或延迟执行的任务。使用单线程确保了任务之间不会并发执行,适用于需要顺序执行的任务。
  1. executor.scheduleAtFixedRate(() -> { ... }, 0, 100, TimeUnit.MILLISECONDS);
  • 安排一个任务以固定的频率(这里是每100毫秒)执行。第一个参数是一个 Runnable 实现,即要执行的任务;第二个参数是初始延迟时间(这里设置为0,意味着立即开始第一次执行);第三个参数是每次执行之间的间隔时间;最后一个参数指定了时间单位。
  1. HikariPoolMXBean hikariPoolMXBean = dataSource.getHikariPoolMXBean();
  • dataSource 获取 HikariPoolMXBean 对象,这是一个管理接口,提供了访问HikariCP连接池的各种统计信息的方法。dataSource 应该是已经被注入的 HikariDataSource 类型的bean。
  1. if (null != hikariPoolMXBean) { ... }
  • 检查获取到的 HikariPoolMXBean 是否为空。如果不为空,则继续执行下面的日志记录逻辑。这一步是为了防止在连接池未完全初始化或其他异常情况下尝试访问它而导致的潜在错误。
  1. 日志记录部分
  • 使用 log.info(...) 记录当前连接池的状态,包括总的连接数、活跃连接数、空闲连接数以及等待连接的线程数。这些信息对于诊断连接池性能问题非常有用。

注意事项

  • 资源释放:这段代码创建了一个新的 ScheduledExecutorService,但是没有提供相应的机制来关闭这个执行器。如果应用程序停止或者重新部署时没有正确地关闭执行器,可能会导致资源泄漏。建议添加 @PreDestroy 方法来保证执行器可以在bean销毁之前被正确关闭。
import javax.annotation.PreDestroy;

@PreDestroy
public void shutdown() {
    if (executor != null && !executor.isShutdown()) {
        executor.shutdown();
    }
}
  • 性能考虑:每隔100毫秒就查询一次连接池状态可能会产生较高的开销,特别是在高负载环境下。应根据实际情况调整监控频率,避免不必要的性能损失。
  • 线程安全:由于 executor 是一个共享资源,在多线程环境中使用时需要注意线程安全性。不过在这个例子中,因为是单线程调度器,所以不需要额外处理线程安全问题。
  • 日志级别选择:使用 info 级别可能不是最佳选择,因为频繁的日志输出会增加日志文件大小并可能导致性能下降。可以考虑使用更合适的日志级别如 debug 或者仅在特定条件下记录日志。