Druid源码分析(八)-- shrink

10 篇文章 1 订阅

Druid源码分析(八)-- shrink

shrink

连接池在一些情况下会收缩
比如,在0.2.6之后,开始支持动态修改maxActive
当新设置的值小于原来的值,不会立刻关闭超出内容的部分,而是等到DestroyThread调度时做shrink才释放。

// DruidDataSource.java
public void shrink(boolean checkTime, boolean keepAlive) {
        try {
        	// 如果连接被锁了,直接return
            lock.lockInterruptibly();
        } catch (InterruptedException e) {
            return;
        }
   ...
}        

这里的lockInterruptibly 函数,前面几天的源码也有用到,今天查了下资料,发现是这样的:

lockInterruptibly 方法和 lock 方法类似,当有可用锁时会直接得到锁并立即返回,如果没有可用锁会一直等待直到获取锁,但和 lock 方法不同,lockInterruptibly 方法在等待获取时,如果遇到线程中断会放弃获取锁,并抛出异常。

获取锁之后,会循环连接池里的连接

// 收缩的连接数 = 连接池中存储的连接数-最小空闲连接数
final int checkCount = poolingCount - minIdle;
final long currentTimeMillis = System.currentTimeMillis();
for (int i = 0; i < poolingCount; ++i) {
    DruidConnectionHolder connection = connections[i];
	
	// 如果发生了致命异常,并且最后发生致命异常的时间大于连接创建的时间,就把连接加入保活连接池
    if ((onFatalError || fatalErrorIncrement > 0) && (lastFatalErrorTimeMillis > connection.connectTimeMillis))  {
        keepAliveConnections[keepAliveCount++] = connection;
        continue;
    }
    // checkTime 默认是false
	if (checkTime) {
	...
	} else {
		// 把要收缩的连接加入驱逐连接池
        if (i < checkCount) {
            evictConnections[evictCount++] = connection;
        } else {
            break;
        }
    }
	
	// 要移除的连接数=收缩的连接数+保活连接数
	int removeCount = evictCount + keepAliveCount;
    if (removeCount > 0) {
    	// 移除连接
        System.arraycopy(connections, removeCount, connections, 0, poolingCount - removeCount);
        // 把连接池中移除的连接都置为null
        Arrays.fill(connections, poolingCount - removeCount, poolingCount, null);
        // 连接池中的连接数减掉移除的连接数
        poolingCount -= removeCount;
    }
    keepAliveCheckCount += keepAliveCount;

	// 如果连接池里的连接数 + activeCount < 最小空闲连接,则需要生成新连接,把needFill 置为true
    if (keepAlive && poolingCount + activeCount < minIdle) {
        needFill = true;
    }
finally {
	// 这里解锁函数开头获取的锁
    lock.unlock();
}

驱逐连接

// 如果驱逐连接数大于0,就调用JdbcUtils 真正关闭连接
if (evictCount > 0) {
    for (int i = 0; i < evictCount; ++i) {
        DruidConnectionHolder item = evictConnections[i];
        Connection connection = item.getConnection();
        JdbcUtils.close(connection);
        // 关闭连接数+1
        destroyCountUpdater.incrementAndGet(this);
    }
    // 把驱逐连接池里的数据都置为null
    Arrays.fill(evictConnections, null);
}

保活连接

// 如果保活连接数大于0 
if (keepAliveCount > 0) {
    // keep order
    for (int i = keepAliveCount - 1; i >= 0; --i) {
    	// 获取连接
        DruidConnectionHolder holer = keepAliveConnections[i];
        Connection connection = holer.getConnection();
        holer.incrementKeepAliveCheckCount();

        boolean validate = false;
        try {
        	// 验证连接的有效性
            this.validateConnection(connection);
            validate = true;
        } 
		...
        boolean discard = !validate;
        if (validate) {
            holer.lastKeepTimeMillis = System.currentTimeMillis();
            // 连接有效的话会把连接放在连接池最后
            boolean putOk = put(holer, 0L, true);
            // 如果连接没有成功加入连接池,就把丢弃标识置为true
            if (!putOk) {
                discard = true;
            }
        }

        if (discard) {
            try {
            	// 丢弃的连接会走回收逻辑
                connection.close();
            } catch (Exception e) {
                // skip
            }

            lock.lock();
            try {
            	// 丢弃连接数+1
                discardCount++;
				// 如果连接池里的连接数 + activeCount < 最小空闲连接,会生成新连接
                if (activeCount + poolingCount <= minIdle) {
                    emptySignal();
                }
            } finally {
                lock.unlock();
            }
        }
    }
    // 保活连接数+
    this.getDataSourceStat().addKeepAliveCheckCount(keepAliveCount);
    Arrays.fill(keepAliveConnections, null);
}

填充连接

// 如果填充标识为true
if (needFill) {
   	lock.lock();
    try {
    	// 根据最小空闲连接 - (活跃连接+连接池里的连接+正在创建的连接)
        int fillCount = minIdle - (activeCount + poolingCount + createTaskCount);
        for (int i = 0; i < fillCount; ++i) {
        	// 创建连接
            emptySignal();
        }
    } finally {
        lock.unlock();
    }
} else if (onFatalError || fatalErrorIncrement > 0) {
	// 不需要填充但是有致命异常的话,也生成新连接
    lock.lock();
    try {
        emptySignal();
    } finally {
        lock.unlock();
    }
}

总结

这个shrink 函数先是用 lockInterruptibly 来获取锁,然后就是判断poolingCount、minIdle这些参数,和有没有致命异常等,去关闭连接、保活连接、创建连接。这个函数也分段用了很多次锁,再次强调了锁的粒度要尽可能小。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值