Druid创建连接的过程
Druid是通过一个创建连接线程来完成连接,如下图所示:
Druid有且只有一个线程来创建连接,为了防止不必要的线程时间片的消耗,其采用了await()/notify()的方式,当其创建了足够的多的连接之后就处于调用await(),使得线程处于blocked状态。当其接收到其它线程的notify()信号之后,才开始重新创建新的连接。Java的线程机制如下图所示:
值得注意的是:Java线程模型中,wait()是到等待队列状态,而获取到notify()信号是到锁池状态,而不是可运行态。这个锁池状态类似于C中的condition条件变量,保证了仅仅有一个线程获取到信号并进入可运行的状态。
Druid获取连接的过程
上层应用在获取MySql Connection的时候是调用的getConnection接口,其内部具体的过程如上图的左半部分所示,值得注意的是其创建连接过程中
a) 首先是从连接池中获取已有的连接。
b) 如果连接池中有连接,则检查是否可用,不可用丢弃并重新获取连接,可用直接返回。
c) 如果连接池中没有连接,则对创建连接线程notify(),并等待一个最大的超时时间。如果在这个超时时间之内,创建连接线程给了其一个notify()信号的话,表明已经有了可用连接,则当前获取连接的线程进行到可运行状态并从连接池中拿到这个连接。中间过程是有lock保证的,所以不会有多线程争用一个的情况。
d) 如果超过了最大超时时间,则抛出异常"create connection error",这个错误和线上日志打印出来的情况一致。
Druid删除不可用连接的过程
Druid数据源连接池有三地方删除不可用连接:
a) 删除不可用连接线程(DestroyConnectionThread)
b) 获取连接后进行连接测试的过程
c) 连接在上层运行过程中报出RuntimeException
Druid的删除不可用连接线程如下图所示:
在"收缩连接池"的过程中,会根据当前连接的使用情况,删除不必要的连接。具体算法是,遍历连接,发现当前拿到的连接的idleTime大于所设置的minEvictableIdleTimeMillis里面。则将其加入到删除连接的列表里面。最后遍历列表进行jdbcUtils.close。
在下一个阶段"删除疑似连接泄露的连接过程中,则是判断一个可用连接已经>最大能容忍的使用连接的时间(这个时间通常设置的很长),如果是则直接close掉此连接。因为这个连接可能运行了某些SQL,导致连接泄露的情况(这一阶段可配置)。
其它两种删除连接的情况都是调用discardConnection这个函数,不同的是(3)是在抛出runTimeException的时候再处理这个异常的时候才调用discardConnection,并打印出"discard connection"
## 原文链接