连接池的最核心的一个功能就是连接管理。我们先来看看在druid中获取连接是怎样的一个实现过程。先来一张图对整个连接获取的过程进行描绘
连接获取与创建
(右键打开新标签查看大图)
客户端通过dataSource.getConnection获取连接,连接获取的具体实现逻辑如下:
1、判断是否有Filter,如果存在通过filterChain获取连接,否则通过getConnectionDirect方法获取连接。
2、获取全局锁lock。数据源中拥有很多数据源相关的属性,而数据源又是一个高并发的环境中使用的,所以需要先获取到数据源的锁才能进行连接的获取。
3、connections数组存放存放当前数据源可用的连接,假如当前connections有可用的连接(通过poolingCount判断),就直接返回连接,递减connections中可用连接的数量
4、当connections中已经没有可用的连接时,就需要进行notEmpty条件等待,并且进行empty的唤醒。因为连接的创建是另外的线程的工作。通常是CreateConnectionThread线程负责连接的创建。
当前线程使用了一个notEmpty的条件进行等待,释放lock。CreateConnectionThread在有足够的可用连接时是进行等待,等待条件empty的唤醒。所以当当前线程进行empty.signal时,CreateConnectionThread从等待中被唤醒,创建连接。连接创建完后进行notEmpty的条件唤醒,然后继续进行empty的条件等待。需要获取连接的线程被notEmpty.signal唤醒后(唤醒需要重新获取锁,有可能被其他线程获取到锁,从而获取了刚刚生成的连接,那么线程将从头开始进行等待)从connections中获取连接。
从池中获取连接后
- 获取之前
- 判断dataSource是否close
- 判断dataSource是否enable
- 判断等待获取连接线程数是否已经大于maxWaitThreadCount
- 创建物理连接后
- 设置autoCommit
- 设置readOnly
- 设置事务隔离
- 执行初始化sql:connectionInitSqls
- 执行验证sql:validationQuery
- 获取到连接后
- testOnBorrow。执行validationQuery
- testWhileIdle。 默认连接最大空余时间:60s。空余时间 = currentTimeMillis - lastActiveTimeMillis。 如果超出了最大空余时间,那么就丢弃这个连接(同时关闭这个连接),重新获取。 为什么要这样做呢?是因为mysql也有这样的限制, 超过最大空闲就不能用了么?那也没有看到初始化的时候配置msql这个属性啊。 同样是使用validationQuery继续测试。
最后
从源码的分析看到,druid设计connections时,connections中的连接是通过创建时间排序的。CreateConnectionThread创建出来的线程是追加到connections的尾部,也就是说connections中第一个连接创建时间是最长的,最后一个连接是最新创建的连接。
同时获取连接时,也是从connections的尾端获取的,最新创建的连接反而会最先被获取。为什么需要这样设计,可以从shrink方法中看到一些原因。在后续的文章中会介绍。