写点什么

druid 源码阅读 8——过一下流程图的 init

作者:张大彪
  • 2022 年 5 月 18 日
  • 本文字数:1976 字

    阅读完需:约 6 分钟

将流程图中的代码串一下,看看 druid 在讲一个什么故事。

public DruidPooledConnection getConnection(long maxWaitMillis) throws SQLException {  init();
if (filters.size() > 0) { FilterChainImpl filterChain = new FilterChainImpl(this); return filterChain.dataSource_connect(this, maxWaitMillis); } else { return getConnectionDirect(maxWaitMillis); }}
复制代码

三个步骤:

  • init() 初始化连接池

  • 遍历 FilterChain,调用 filterChain.dataSource_connect(this, maxWaitMillis), 这不是研究重点,不展开。

  • getConnectionDirect(maxWaitMillis)来活的真正的连接对象。


分步拆解:

一、init() 初始化连接池

1、判断 DruidDataSource 的 init 状态是否为 true?

是:连接池已经被初始化过了,直接 return

否:连接池还没被初始化过,只能先初始化连接池,再从中后去 DruidPolledConnection

2、调用 DruidDriver.createDataSourceId() 来生成 datasourceId

3、循环遍历所有的 FilterChain 上的每一个 Filter,让每个 Filter 调用 init(自己,当前 datasource)方法,从而能拿到数据源属性。

4、从 jdbcURL 中获得 dbTypeName, dbTypeName 是以下 4 个中的一个的,做特殊处理(mysql, mariadb,OceanBase,ads)。处理逻辑是:如果 connectionProperties 中包含 key=cacheServerConfiguration,则将其设为 true

5、入参检查,只要不符合要求,就抛异常 maxActive <= 0, 抛。 maxActive < minIdle, 抛。 连接池的初始 size > maxActive, 抛。 maxEvictableIdleTimeMillis < minEvictableIdleTimeMillis, 抛

6、调用 initFromSPIServiceLoader()方法,通过 SPI 机制获取额外的 Filter,并加入到 filters

7、调用 resolveDriver()来解析数据库驱动,所以即便不配置 driverClass 也没关系,因为会从 jdbcURL 中解析出对应的 driverClassName

8、调用 initCheck()来做两件事情:

1)根据 dbTypeName 来识别出到底是什么数据库

2)根据数据库和配置的 validationQuery,利用 SQLStatementParser 来检查配置的 validationQuery 是否和对应的数据库匹配。

旁白:这里能检查出很多问题,比如 ob 不支持 select 1 from dual。比如 mysql 不支持 select * from dual 等

9、其他的 check

10、构造全局存连接对象的数据结构:

connections = new DruidConnectionHolder[maxActive];evictConnections = new DruidConnectionHolder[maxActive];keepAliveConnections = new DruidConnectionHolder[maxActive];
复制代码

11、初始化连接池的连接知道满足最低要求 initialSize

if (createScheduler != null && asyncInit) {    for (int i = 0; i < initialSize; ++i) {        submitCreateTask(true);    }} else if (!asyncInit) {    // init connections    while (poolingCount < initialSize) {        try {            PhysicalConnectionInfo pyConnectInfo = createPhysicalConnection();            DruidConnectionHolder holder = new DruidConnectionHolder(this, pyConnectInfo);            connections[poolingCount++] = holder;        } catch (SQLException ex) {            LOG.error("init datasource error, url: " + this.getUrl(), ex);            if (initExceptionThrow) {                connectError = ex;                break;            } else {                Thread.sleep(3000);            }        }    }
if (poolingCount > 0) { poolingPeak = poolingCount; poolingPeakTime = System.currentTimeMillis(); }}
复制代码

12、开启一组统计信息,创建连接,清理连接的线程

createAndLogThread();createAndStartCreatorThread();createAndStartDestroyThread();
复制代码

13、registerMbean 注册 druid 的 Mbean,所以 JMX 可以用来监控 Druid 连接池了。//这块没看懂,有空的时候看一下吧。

public void registerMbean() {    if (!mbeanRegistered) {        AccessController.doPrivileged(new PrivilegedAction<Object>() {
@Override public Object run() { ObjectName objectName = DruidDataSourceStatManager.addDataSource(DruidDataSource.this, DruidDataSource.this.name);
DruidDataSource.this.setObjectName(objectName); DruidDataSource.this.mbeanRegistered = true;
return null; } }); }}
复制代码


14、判断连接池的 keepAlive 开关是否打开?

是:说明连接池至少要保留 minIdle 个空闲连接,(异步执行)基于 minIdle 的值,利用 CreateConnection 线程来创建连接,知道创建 minIdle 个连接为止。

否:初始化连接池成功,把 inited 设置为 true。

用户头像

张大彪

关注

还未添加个人签名 2018.04.25 加入

还未添加个人简介

评论

发布
暂无评论
druid 源码阅读 8——过一下流程图的init_张大彪_InfoQ写作社区