再有人问你数据库连接池 Druid 的原理,这篇文章甩给他!( 二 )

3)关闭数据源
dataSource.close();3 连接池 Druid 实现原理我们学习数据源的实现,可以从如下五个核心角度分析:

  • 初始化
  • 创建连接
  • 回收连接
  • 归还连接
  • 销毁连接
3.1 初始化首先我们查看数据源实现「获取连接」的接口,初始化可以主动和被动两种方式 。
主从是指显示的调用 init 方法,而被动是指获取连接时完成初始化 。
再有人问你数据库连接池 Druid 的原理,这篇文章甩给他!

文章插图
图片
调用getConnection方法时,返回的对象是连接接口的封装类 DruidConnectionHolder  。
在初始化方法内,数据源创建三个连接池数组 ,他们分别是:
再有人问你数据库连接池 Druid 的原理,这篇文章甩给他!

文章插图
图片
  • connections:用于存放能获取的连接对象 。
  • evictConnections:用于存放需要丢弃的连接对象 。
  • keepAliveConnections:用于存放需要保活的连接对象 。
初始化阶段,需要进行连接池的「预热」:也就是需要按照配置首先创建一定数量的连接,并放入到池子里,这样应用在需要获取连接的候,可以直接从池子里获取 。
数据源「预热」分为同步和异步两种方式  ,见下图:
再有人问你数据库连接池 Druid 的原理,这篇文章甩给他!

文章插图
图片
从上图,我们可以看到同步创建连接时,是原生 JDBC 创建连接后,直接放入到 connections 数组对象里 。
异步创建线程需要初始化 createScheduler , 但默认并没有配置 。
数据源预热之后,启动了两个任务线程:创建连接线程和销毁连接线程 。
再有人问你数据库连接池 Druid 的原理,这篇文章甩给他!

文章插图
图片
3.2 创建连接这一节,我们重点学习 Druid 数据源如何创建连接 。
CreateConnectionThread 本质是一个单线程在死循环中通过 condition 等待,被其他线程唤醒 ,并实现创建数据库连接逻辑 。
再有人问你数据库连接池 Druid 的原理,这篇文章甩给他!

文章插图
图片
笔者将 run 方法做了适当简化,当满足了条件之后,才创建数据库连接 :
  • 必须存在线程等待 , 才创建连接。
  • 防止创建超过最大连接数 maxAcitve。
创建完连接对象 PhysicalConnectionInfo 之后 , 需要保存到 Connections 数组里,并唤醒到其他的线程,这样就可以从池子里获取连接 。
再有人问你数据库连接池 Druid 的原理,这篇文章甩给他!

文章插图
图片
3.3 获取连接我们详细解析了创建连接的过程 , 接下来就是应用如何获取连接的过程 。
DruidDataSource#getConnection 方法会调用到 DruidDataSource#getConnectionDirect 方法来获取连接,实现如下所示 。
再有人问你数据库连接池 Druid 的原理,这篇文章甩给他!

文章插图
图片
核心流程是
1)在 for 循环内,首先调用 getConnectionDirect内,调用getConnectionInternal 从池子里获取连接对象;
2)获取连接后,需要根据 testOnBorrow 、testWhileIdle 参数配置判断是否需要检测连接的有效性;
3)最后假如需要判断连接是否有泄露,则配置 removeAbandoned 来关闭长时间不适用的连接,该功能不建议再生产环境中使用 , 仅用于连接泄露检测诊断 。
接下来进入获取连接的重点:getConnectionInternal 方法如何从池子里获取连接 。
再有人问你数据库连接池 Druid 的原理,这篇文章甩给他!

文章插图
图片
getConnectionInternal()方法中拿到连接的方式有三种: