Druid源码分析(五) 初始化连接 initialSize

设置每次统计前重置所有统计变量

dataSourceStat.setResetStatEnable(this.resetStatEnable);

声明的三个DruidConnectionHolder数组代表什么

// 记录存放数据库链接
connections = new DruidConnectionHolder[maxActive];
// 记录被剔除的数据库链接
evictConnections = new DruidConnectionHolder[maxActive];
// 记录检测后存活的链接
keepAliveConnections = new DruidConnectionHolder[maxActive];

启动时异步创建线程池

// 需要在properties里配置createScheduler 并且 asyncInit =true,默认asyncInit=false
if (createScheduler != null && asyncInit) {
                for (int i = 0; i < initialSize; ++i) {
                  // 此处逻辑和下面同步差不多
                    submitCreateTask(true);
                }
}

启动时同步创建连接池

默认initialSize=0 需要DruidDataSource.setInitialSize(?),并且设置的数值是不能超过maxActive(默认8)的,否则报错.
为什么需要initialSize,我觉得是为了应对流量比较大的应用,可以考虑在启动时候就开启连接池,否则会遇到第一条sql特别慢的情况,造成不必要的slowsql统计.
dataSourceStat.setResetStatEnable(this.resetStatEnable);

            connections = new DruidConnectionHolder[maxActive];
            evictConnections = new DruidConnectionHolder[maxActive];
            keepAliveConnections = new DruidConnectionHolder[maxActive];

            SQLException connectError = null;

            // 异步初始化 需要properties里配置createScheduler和asyncInit设置为true,这个是兼容老版本,异步
            if (createScheduler != null && asyncInit) {
                for (int i = 0; i < initialSize; ++i) {
                    submitCreateTask(true);
                }
            } else if (!asyncInit) {
                // initialSize 默认0 指的是初始化时候的大小,如果指定会创建链接对象.否则会再调用的时候创建
                while (poolingCount < initialSize) {
                    try {
                        // 创建物理链接 封装了 datasource的connection信息
                        PhysicalConnectionInfo pyConnectInfo = createPhysicalConnection();
                        // 为每个connection设置对应的holder connectionId是key 记录了每个链接上一次链接的信息
                        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);
                        }
                    }
                }

                // peak代表峰值 供统计参考
                if (poolingCount > 0) {
                    poolingPeak = poolingCount;
                    poolingPeakTime = System.currentTimeMillis();
                }
            }

            // 创建统计连接池状态的线程,间隔是 timeBetweenLogStatsMillis
            createAndLogThread();
            // 异步 初始化从连接池里获取链接的线程 = 从连接池拿物理链接并且put到map的holder中 保证不会被gc
            createAndStartCreatorThread();

            // 异步 初始化从连接池里销毁的线程 间隔为 timeBetweenEvictionRunsMillis
            createAndStartDestroyThread();

            // 堵塞 createAndStartCreatorThread和createAndStartDestroyThread 都会countDown 也就是说等这2个线程初始化完成才会做这个
            initedLatch.await();
            init = true;

创建连接池

同步和异步创建最后都会走到这
public void run() {
            // 使latch的值减1,如果减到了0,则会唤醒所有等待在这个latch上的线程
            initedLatch.countDown();
            System.out.println("CreateConnectionThread initedLatch countDown " + initedLatch.getCount());

            long lastDiscardCount = 0;
            int errorCount = 0;
            for (; ; ) {
                // addLast
                try {
                    lock.lockInterruptibly();
                } catch (InterruptedException e2) {
                    // 中断死循环
                    break;
                }

                long discardCount = DruidDataSource.this.discardCount;
                boolean discardChanged = discardCount - lastDiscardCount > 0;
                lastDiscardCount = discardCount;

                try {
                    // true代表不允许创建
                    boolean emptyWait = true;

                    // 开始各种创建连接池的判断
                    if (createError != null && poolingCount == 0 && !discardChanged) {
                        // 如果创建时没发生错误 并且 当前池子里也没有链接 并且 丢弃链接的数量大于0
                        emptyWait = false;
                    }

                    // 一般asyncInit=false 不会异步任务创建. initialSize也是0
                    if (emptyWait && asyncInit && createCount < initialSize) {
                        // 如果是异步创建的方式 并且 创建的数量小于初始化的数量,那么也允许创建连接
                        emptyWait = false;
                    }

                    // 初始化时候核心逻辑,有一个false则允许创建
                    if (emptyWait) {
                        // 当前连接池里可用的链接数 大于等于 消费等待的线程数,初始化时 poolingCount >= notEmptyWaitThreadCount 代表有需求者要连接
                        if (poolingCount >= notEmptyWaitThreadCount
                                // 如果开启了keepAlive 并且活动的连接池+可用的连接池数 小于 最小的空闲连接数
                                && (!(keepAlive && activeCount + poolingCount < minIdle))
                                // 如果没有连续的错误
                                && !isFailContinuous()) {
                            // 堵塞 不能创建

                            System.out.println("堵塞 empty.await()");

                            empty.await();
                        }

                        System.out.println("empty 唤醒");

                        // 防止创建超过maxActive数量的连接
                        if (activeCount + poolingCount >= maxActive) {
                            // 堵塞 不能创建
                            System.out.println("堵塞生产者 活动链接 + 当前可用链接 大于等于 maxActive");
                            empty.await();
                            continue;
                        }
                    }

                } catch (InterruptedException e) {
                    lastCreateError = e;
                    lastErrorTimeMillis = System.currentTimeMillis();

                    if ((!closing) && (!closed)) {
                        LOG.error("create connection Thread Interrupted, url: " + jdbcUrl, e);
                    }
                    break;
                } finally {
                    lock.unlock();
                }

                PhysicalConnectionInfo connection = null;

                // 创建链接
                try {
                    connection = createPhysicalConnection();
                } catch (SQLException e) {
                    LOG.error("create connection SQLException, url: " + jdbcUrl + ", errorCode " + e.getErrorCode()
                            + ", state " + e.getSQLState(), e);

                    errorCount++;
                    // timeBetweenConnectErrorMillis = 500ms
                    if (errorCount > connectionErrorRetryAttempts && timeBetweenConnectErrorMillis > 0) {
                        // fail over retry attempts
                        setFailContinuous(true);
                        if (failFast) {
                            lock.lock();
                            try {
                                notEmpty.signalAll();
                            } finally {
                                lock.unlock();
                            }
                        }

                        if (breakAfterAcquireFailure) {
                            break;
                        }

                        try {
                            Thread.sleep(timeBetweenConnectErrorMillis);
                        } catch (InterruptedException interruptEx) {
                            break;
                        }
                    }
                } catch (RuntimeException e) {
                    LOG.error("create connection RuntimeException", e);
                    setFailContinuous(true);
                    continue;
                } catch (Error e) {
                    LOG.error("create connection Error", e);
                    setFailContinuous(true);
                    break;
                }

                // 如果创建错误 那么connection 是空,因为catch了错误
                if (connection == null) {
                    continue;
                }

                boolean result = put(connection);
                if (!result) {
                    JdbcUtils.close(connection.getPhysicalConnection());
                    LOG.info("put physical connection to pool failed.");
                }

                errorCount = 0; // reset errorCount

                if (closing || closed) {
                    break;
                }
            }
        }
    }
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,591评论 6 501
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,448评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,823评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,204评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,228评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,190评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,078评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,923评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,334评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,550评论 2 333
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,727评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,428评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,022评论 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,672评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,826评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,734评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,619评论 2 354

推荐阅读更多精彩内容