本文主要研究一个hikari连接池的idleTimeout及minimumIdle属性

idleTimeout

默认是600000毫秒,即10分钟。如果idleTimeout+1秒>maxLifetime 且 maxLifetime>0,则会被重置为0;如果idleTimeout!=0且小于10秒,则会被重置为10秒。如果idleTimeout=0则表示空闲的连接在连接池中永远不被移除。

只有当minimumIdle小于maximumPoolSize时,这个参数才生效,当空闲连接数超过minimumIdle,而且空闲时间超过idleTimeout,则会被移除。

minimumIdle

控制连接池空闲连接的最小数量,当连接池空闲连接少于minimumIdle,而且总共连接数不大于maximumPoolSize时,HikariCP会尽力补充新的连接。为了性能考虑,不建议设置此值,而是让HikariCP把连接池当做固定大小的处理,默认minimumIdle与maximumPoolSize一样。

当minIdle<0或者minIdle>maxPoolSize,则被重置为maxPoolSize,该值默认为10。

HikariPool.HouseKeeper

HikariCP-2.7.6-sources.jar!/com/zaxxer/hikari/pool/HikariPool.java

private final long HOUSEKEEPING_PERIOD_MS = Long.getLong("com.zaxxer.hikari.housekeeping.periodMs", SECONDS.toMillis(30));

this.houseKeeperTask = houseKeepingExecutorService.scheduleWithFixedDelay(new HouseKeeper(), 100L, HOUSEKEEPING_PERIOD_MS, MILLISECONDS);

/**

* The house keeping task to retire and maintain minimum idle connections.

*/

private final class HouseKeeper implements Runnable

{

private volatile long previous = plusMillis(currentTime(), -HOUSEKEEPING_PERIOD_MS);

@Override

public void run()

{

try {

// refresh timeouts in case they changed via MBean

connectionTimeout = config.getConnectionTimeout();

validationTimeout = config.getValidationTimeout();

leakTaskFactory.updateLeakDetectionThreshold(config.getLeakDetectionThreshold());

final long idleTimeout = config.getIdleTimeout();

final long now = currentTime();

// Detect retrograde time, allowing +128ms as per NTP spec.

if (plusMillis(now, 128) < plusMillis(previous, HOUSEKEEPING_PERIOD_MS)) {

LOGGER.warn("{} - Retrograde clock change detected (housekeeper delta={}), soft-evicting connections from pool.",

poolName, elapsedDisplayString(previous, now));

previous = now;

softEvictConnections();

return;

}

else if (now > plusMillis(previous, (3 * HOUSEKEEPING_PERIOD_MS) / 2)) {

// No point evicting for forward clock motion, this merely accelerates connection retirement anyway

LOGGER.warn("{} - Thread starvation or clock leap detected (housekeeper delta={}).", poolName, elapsedDisplayString(previous, now));

}

previous = now;

String afterPrefix = "Pool ";

if (idleTimeout > 0L && config.getMinimumIdle() < config.getMaximumPoolSize()) {

logPoolState("Before cleanup ");

afterPrefix = "After cleanup ";

final List notInUse = connectionBag.values(STATE_NOT_IN_USE);

int toRemove = notInUse.size() - config.getMinimumIdle();

for (PoolEntry entry : notInUse) {

if (toRemove > 0 && elapsedMillis(entry.lastAccessed, now) > idleTimeout && connectionBag.reserve(entry)) {

closeConnection(entry, "(connection has passed idleTimeout)");

toRemove--;

}

}

}

logPoolState(afterPrefix);

fillPool(); // Try to maintain minimum connections

}

catch (Exception e) {

LOGGER.error("Unexpected exception in housekeeping task", e);

}

}

}

这个HouseKeeper是一个定时任务,在HikariPool构造器里头初始化,默认的是初始化后100毫秒执行,之后每执行完一次之后隔HOUSEKEEPING_PERIOD_MS(30秒)时间执行。

这个定时任务的作用就是根据idleTimeout的值,移除掉空闲超时的连接。

首先检测时钟是否倒退,如果倒退了则立即对过期的连接进行标记evict;之后当idleTimeout>0且配置的minimumIdle

取出状态是STATE_NOT_IN_USE的连接数,如果大于minimumIdle,则遍历STATE_NOT_IN_USE的连接的连接,将空闲超时达到idleTimeout的连接从connectionBag移除掉,若移除成功则关闭该连接,然后toRemove--。

在空闲连接移除之后,再调用fillPool,尝试补充空间连接数到minimumIdle值

HikariPool.fillPool

HikariCP-2.7.6-sources.jar!/com/zaxxer/hikari/pool/HikariPool.java

private final PoolEntryCreator POOL_ENTRY_CREATOR = new PoolEntryCreator(null /*logging prefix*/);

private final PoolEntryCreator POST_FILL_POOL_ENTRY_CREATOR = new PoolEntryCreator("After adding ");

LinkedBlockingQueue addConnectionQueue = new LinkedBlockingQueue<>(config.getMaximumPoolSize());

this.addConnectionQueue = unmodifiableCollection(addConnectionQueue);

this.addConnectionExecutor = createThreadPoolExecutor(addConnectionQueue, poolName + " connection adder", threadFactory, new ThreadPoolExecutor.DiscardPolicy());

/**

* Fill pool up from current idle connections (as they are perceived at the point of execution) to minimumIdle connections.

*/

private synchronized void fillPool()

{

final int connectionsToAdd = Math.min(config.getMaximumPoolSize() - getTotalConnections(), config.getMinimumIdle() - getIdleConnections())

- addConnectionQueue.size();

for (int i = 0; i < connectionsToAdd; i++) {

addConnectionExecutor.submit((i < connectionsToAdd - 1) ? POOL_ENTRY_CREATOR : POST_FILL_POOL_ENTRY_CREATOR);

}

}

PoolEntryCreator

/**

* Creating and adding poolEntries (connections) to the pool.

*/

private final class PoolEntryCreator implements Callable

{

private final String loggingPrefix;

PoolEntryCreator(String loggingPrefix)

{

this.loggingPrefix = loggingPrefix;

}

@Override

public Boolean call() throws Exception

{

long sleepBackoff = 250L;

while (poolState == POOL_NORMAL && shouldCreateAnotherConnection()) {

final PoolEntry poolEntry = createPoolEntry();

if (poolEntry != null) {

connectionBag.add(poolEntry);

LOGGER.debug("{} - Added connection {}", poolName, poolEntry.connection);

if (loggingPrefix != null) {

logPoolState(loggingPrefix);

}

return Boolean.TRUE;

}

// failed to get connection from db, sleep and retry

quietlySleep(sleepBackoff);

sleepBackoff = Math.min(SECONDS.toMillis(10), Math.min(connectionTimeout, (long) (sleepBackoff * 1.5)));

}

// Pool is suspended or shutdown or at max size

return Boolean.FALSE;

}

/**

* We only create connections if we need another idle connection or have threads still waiting

* for a new connection. Otherwise we bail out of the request to create.

*

* @return true if we should create a connection, false if the need has disappeared

*/

private boolean shouldCreateAnotherConnection() {

return getTotalConnections() < config.getMaximumPoolSize() &&

(connectionBag.getWaitingThreadCount() > 0 || getIdleConnections() < config.getMinimumIdle());

}

}

shouldCreateAnotherConnection方法决定了是否需要添加新的连接

createPoolEntry

/**

* Creating new poolEntry. If maxLifetime is configured, create a future End-of-life task with 2.5% variance from

* the maxLifetime time to ensure there is no massive die-off of Connections in the pool.

*/

private PoolEntry createPoolEntry()

{

try {

final PoolEntry poolEntry = newPoolEntry();

final long maxLifetime = config.getMaxLifetime();

if (maxLifetime > 0) {

// variance up to 2.5% of the maxlifetime

final long variance = maxLifetime > 10_000 ? ThreadLocalRandom.current().nextLong( maxLifetime / 40 ) : 0;

final long lifetime = maxLifetime - variance;

poolEntry.setFutureEol(houseKeepingExecutorService.schedule(

() -> {

if (softEvictConnection(poolEntry, "(connection has passed maxLifetime)", false /* not owner */)) {

addBagItem(connectionBag.getWaitingThreadCount());

}

},

lifetime, MILLISECONDS));

}

return poolEntry;

}

catch (Exception e) {

if (poolState == POOL_NORMAL) { // we check POOL_NORMAL to avoid a flood of messages if shutdown() is running concurrently

LOGGER.debug("{} - Cannot acquire connection from data source", poolName, (e instanceof ConnectionSetupException ? e.getCause() : e));

}

return null;

}

}

createPoolEntry方法创建一个poolEntry,同时给它的lifetime过期设定了一个延时任务。

小结

HouseKeeper是一个定时任务,在HikariPool构造器里头初始化,默认的是初始化后100毫秒执行,之后每执行完一次之后隔HOUSEKEEPING_PERIOD_MS(30秒)时间执行。

如果发现时钟倒退,则立即标记evict连接,然后退出;否则都会执行fillPool,来试图维持空闲连接到minimumIdle的数值

当idleTimeout>0且配置的minimumIdle

当minIdle<0或者minIdle>maxPoolSize,则minIdle被重置为maxPoolSize,该值默认为10,官方建议设置为一致,当做固定大小的连接池处理提高性能

idleTimeout有点类似tomcat jdbc pool里头的min-evictable-idle-time-millis参数。不同的是tomcat jdbc pool的连接泄露检测以及空闲连接清除的工作都放在一个名为PoolCleaner的timerTask中处理,该任务的执行间隔为timeBetweenEvictionRunsMillis,默认为5秒;而hikari的连接泄露是每次getConnection的时候单独触发一个延时任务来处理,而空闲连接的清除则是使用HouseKeeper定时任务来处理,其运行间隔由com.zaxxer.hikari.housekeeping.periodMs环境变量控制,默认为30秒。

doc

php 连接池 idletime,聊聊hikari连接池的idleTimeout及minimumIdle属性相关推荐

  1. 聊聊hikari连接池的isAllowPoolSuspension

    序 本文主要研究一下hikari连接池的isAllowPoolSuspension属性 实例代码 @Testpublic void testPoolSuspend() throws SQLExcept ...

  2. 聊聊hikari连接池的maxLifetime属性及evict操作

    序 本文主要研究一下hikari连接池的maxLifetime属性及evict操作 maxLifetime属性及evict操作 maxLifetime 用来设置一个connection在连接池中的存活 ...

  3. 聊聊hikari连接池的maxLifetime属性及evict操作 1

    序 本文主要研究一下hikari连接池的maxLifetime属性及evict操作 maxLifetime属性及evict操作 maxLifetime 用来设置一个connection在连接池中的存活 ...

  4. hikaripool连接保持_springboot2的hikari数据库连接池默认配置

    序 Spring-Boot-2.0.0-M1版本将默认的数据库连接池从tomcat jdbc pool改为了hikari,这里主要研究下hikari的默认配置 spring-configuration ...

  5. 【追光者系列】Hikari连接池大小多大合适?(第一弹)

    摘要: 原创出处微信公众号 「工匠小猪猪的技术世界」欢迎转载,保留摘要,谢谢! 1.这是一个系列,有兴趣的朋友可以持续关注 2.如果你有HikariCP使用上的问题,可以给我留言,我们一起沟通讨论 3 ...

  6. Hikari连接池——java.lang.Exception: Apparent connection leak detected

    Hikari连接池--java.lang.Exception: Apparent connection leak detected 问题分析 总结 问题分析 首先,先看报错: java.lang.Ex ...

  7. 微服务架构师封神之路09-Springboot多数据源,Hikari连接池和事务配置

    微服务架构师封神之路09-Springboot多数据源,Hikari连接池,和事务的配置 application.yml 初始化DataSource DataSourceConfig的两种写法 写法一 ...

  8. spring-boot配置MySQL数据库连接、Hikari连接池、和Mybatis的简单方法

    此方法为极简配置,支持MySQL数据库多库连接.支持Hikari连接池.支持MyBatis(包括Dao类和xml文件位置的配置). 如果需要更灵活的自定义配置(比如支持分页插件),请参考:http:/ ...

  9. Connection is not available, request timed out after xxxms. 超时异常 Hikari连接池配置说明

    ## 数据库配置 spring.datasource.type=com.zaxxer.hikari.HikariDataSource spring.datasource.driverClassName ...

最新文章

  1. datetime处理日期和时间
  2. centos下svn与mysql_centos下SVN搭建
  3. Spring事务管理--嵌套事务详解
  4. JVM Attach机制实现
  5. BZOJ_1629_[Usaco2007_Demo]_Cow_Acrobats_(贪心)
  6. C 标准库—— assert.h
  7. 多线程之-并发任务间交换数据
  8. 一种单片机支持WiFi的应用——SimpleWiFi在单片机中的应用
  9. CISP 考试教材《第 9 章 知识域:计算环境安全》知识整理
  10. python浏览器复制粘贴到word里(带格式的)
  11. RAC修改IP(public/virtual/scan)
  12. 冒泡详解(分析每一步)
  13. C# chart实时曲线
  14. matlab读取mp4视频,【Matlab系列】之视频文件读取和显示的方法
  15. IOS个人开发者账号和wp公司开发者帐号申请注意点
  16. 日本企业遭遇严重用工短缺
  17. 实现网站的高并发访问
  18. python列表get方法_Python json.get方法代码示例
  19. nyist oj nyoj 865
  20. 使用电源管理模块有效控制GaN功率放大器的电源开关

热门文章

  1. 表单中的重置与取消按钮
  2. ubuntu下载gmt_科学网—Linux/Ubuntu安装地学制图软件GMT6.0.0 - 杨家乐的博文
  3. 实现根据条件删除_强大的定位空值法,1秒删除所有不想要的数据
  4. 新海诚没有参与制作的作品_新海诚简介和所有作品列表
  5. linux下载python的地址_Linux下Python获取IP地址的代码
  6. python cv release_cv2.videoCapture.release()是什么意思?
  7. vue4 跳转外部链接_vue跳转到外部链接
  8. 想要酷炫大气的网页设计?这样做超吸睛
  9. 设计灵感|如何做好网页后台数据展示的设计?
  10. 不规则炫彩创意渐变海报设计模板素材|带来十足的时尚感