Druid-代码段-2-1

所属文章:池化技术(一)Druid是如何管理数据库连接的?

 本代码段对应主流程2,具体用来初始化整个连接池:


public void init() throws SQLException {
        if (inited) {
            return; //如果已经被初始化过,则终止该方法
        }

        // bug fixed for dead lock, for issue #2980
        DruidDriver.getInstance();

        final ReentrantLock lock = this.lock; //获取重入锁
        try {
            lock.lockInterruptibly();
        } catch (InterruptedException e) {
            throw new SQLException("interrupt", e);
        }

        boolean init = false;
        try {
            if (inited) { //双重检查
                return;
            }

            initStackTrace = Utils.toString(Thread.currentThread().getStackTrace());

            this.id = DruidDriver.createDataSourceId(); //生成连接池id
            if (this.id > 1) {
                //生成其他对象的id,比如连接对象的id、statement对象的id
                long delta = (this.id - 1) * 100000;
                this.connectionIdSeedUpdater.addAndGet(this, delta);
                this.statementIdSeedUpdater.addAndGet(this, delta);
                this.resultSetIdSeedUpdater.addAndGet(this, delta);
                this.transactionIdSeedUpdater.addAndGet(this, delta);
            }

            if (this.jdbcUrl != null) {
                this.jdbcUrl = this.jdbcUrl.trim();
                initFromWrapDriverUrl(); //jdbc url的头必须是jdbc:wrap-jdbc才会触发该方法里的逻辑(这个头貌似是oracle的?本篇文章仅针对mysql)
            }

            for (Filter filter : filters) {
                filter.init(this); //通过池对象初始化filters(因为filter里面可能会用到一些池属性)
            }

            if (this.dbType == null || this.dbType.length() == 0) {
                this.dbType = JdbcUtils.getDbType(jdbcUrl, null); //根据jdbc协议头分析出当前数据库的类型(本文默认mysql)
            }

            if (JdbcConstants.MYSQL.equals(this.dbType)
                    || JdbcConstants.MARIADB.equals(this.dbType)
                    || JdbcConstants.ALIYUN_ADS.equals(this.dbType)) {
                boolean cacheServerConfigurationSet = false;
                if (this.connectProperties.containsKey("cacheServerConfiguration")) {
                    cacheServerConfigurationSet = true;
                } else if (this.jdbcUrl.indexOf("cacheServerConfiguration") != -1) {
                    cacheServerConfigurationSet = true;
                }
                if (cacheServerConfigurationSet) {
                    this.connectProperties.put("cacheServerConfiguration", "true");
                }
            }

            //下面就是对设置的这些属性合理性的判断,不符合要求的将直接抛异常
            if (maxActive <= 0) {
                throw new IllegalArgumentException("illegal maxActive " + maxActive);
            }

            if (maxActive < minIdle) {
                throw new IllegalArgumentException("illegal maxActive " + maxActive);
            }

            if (getInitialSize() > maxActive) {
                throw new IllegalArgumentException("illegal initialSize " + this.initialSize + ", maxActive " + maxActive);
            }

            if (timeBetweenLogStatsMillis > 0 && useGlobalDataSourceStat) {
                throw new IllegalArgumentException("timeBetweenLogStatsMillis not support useGlobalDataSourceStat=true");
            }

            if (maxEvictableIdleTimeMillis < minEvictableIdleTimeMillis) {
                throw new SQLException("maxEvictableIdleTimeMillis must be grater than minEvictableIdleTimeMillis");
            }

            if (this.driverClass != null) {
                this.driverClass = driverClass.trim();
            }

            //通过SPI机制加载责任链上需要执行的filter,方法详情在下面
            initFromSPIServiceLoader();

            //如果driver为空,加载驱动,最终将加载到的驱动注册到DriverManager上去
            if (this.driver == null) {
                if (this.driverClass == null || this.driverClass.isEmpty()) {
                    this.driverClass = JdbcUtils.getDriverClassName(this.jdbcUrl); //在driverClass不配置的情况下,druid会通过url来判定属于哪个driverClass
                }

                if (MockDriver.class.getName().equals(driverClass)) { //忽略
                    driver = MockDriver.instance;
                } else {
                    if (jdbcUrl == null && (driverClass == null || driverClass.length() == 0)) {
                        throw new SQLException("url not set");
                    }
                    driver = JdbcUtils.createDriver(driverClassLoader, driverClass); //driverClass不为空的情况下直接触发驱动加载
                }
            } else { //除非手动设置驱动,否则不会走这里的逻辑
                if (this.driverClass == null) {
                    this.driverClass = driver.getClass().getName();
                }
            }

            initCheck(); //根据dbType的不同,来初始化一些标记字段(比如isMySql)

            initExceptionSorter(); //异常处理器初始化
            initValidConnectionChecker(); //初始化长连接检测时所需要用到的checker的适配类型,具体实现在下面
            validationQueryCheck(); //简单的检测validationQuery参数是否填写了,若没填写会打印一个错误日志,不影响主流程

            if (isUseGlobalDataSourceStat()) { //默认不开启,忽略
                dataSourceStat = JdbcDataSourceStat.getGlobal();
                if (dataSourceStat == null) {
                    dataSourceStat = new JdbcDataSourceStat("Global", "Global", this.dbType);
                    JdbcDataSourceStat.setGlobal(dataSourceStat);
                }
                if (dataSourceStat.getDbType() == null) {
                    dataSourceStat.setDbType(this.dbType);
                }
            } else {
                dataSourceStat = new JdbcDataSourceStat(this.name, this.jdbcUrl, this.dbType, this.connectProperties);
            }
            dataSourceStat.setResetStatEnable(this.resetStatEnable);

            //下面三个数组都跟池子本身有关系,所以容量为maxActive
            connections = new DruidConnectionHolder[maxActive]; //初始化连接池本体
            evictConnections = new DruidConnectionHolder[maxActive]; //初始化丢弃连接数组(流程4.1需要用到)
            keepAliveConnections = new DruidConnectionHolder[maxActive]; //初始化需要检测可用性连接数组(流程4.1要用)

            SQLException connectError = null;

            if (createScheduler != null && asyncInit) { //另外一种通过线程池管理连接池的方式,默认不启用,忽略
                for (int i = 0; i < initialSize; ++i) {
                    createTaskCount++;
                    CreateConnectionTask task = new CreateConnectionTask(true);
                    this.createSchedulerFuture = createScheduler.submit(task);
                }
            } else if (!asyncInit) {
                // init connections
                while (poolingCount < initialSize) { //当池子里的连接数少于需要初始化的个数时,则需要不断新增连接填充连接池,直到等于初始化连接数
                    try {
                        PhysicalConnectionInfo pyConnectInfo = createPhysicalConnection(); //直接通过驱动程序创建连接对象,参考流程2.1
                        DruidConnectionHolder holder = new DruidConnectionHolder(this, pyConnectInfo); //拿着驱动连接包装成holder对象
                        connections[poolingCount++] = holder; //生成好的连接直接往后排
                    } catch (SQLException ex) {
                        LOG.error("init datasource error, url: " + this.getUrl(), ex);
                        if (initExceptionThrow) {
                            connectError = ex;
                            break;
                        } else {
                            Thread.sleep(3000); //异常报错后会休眠3s来进行下次的添加
                        }
                    }
                }

                if (poolingCount > 0) {
                    poolingPeak = poolingCount;
                    poolingPeakTime = System.currentTimeMillis();
                }
            }

            createAndLogThread(); //开启打印log日志的守护线程
            createAndStartCreatorThread(); //开启负责新增连接的守护线程(主流程3)
            createAndStartDestroyThread(); //开启负责丢弃连接的守护线程(主流程4)

            initedLatch.await(); //倒计数器,用来保证上面的主流程3和4两个守护线程全部开启完毕后才进行接下来的操作
            init = true;

            initedTime = new Date();
            registerMbean();

            if (connectError != null && poolingCount == 0) {
                throw connectError;
            }

            if (keepAlive) {
                // async fill to minIdle
                if (createScheduler != null) { //默认不启用该模式,忽略
                    for (int i = 0; i < minIdle; ++i) {
                        createTaskCount++;
                        CreateConnectionTask task = new CreateConnectionTask(true);
                        this.createSchedulerFuture = createScheduler.submit(task);
                    }
                } else {
                    this.emptySignal(); //keepAlive=true,主动唤起主流程3一次
                }
            }

        } catch (SQLException e) {
            LOG.error("{dataSource-" + this.getID() + "} init error", e);
            throw e;
        } catch (InterruptedException e) {
            throw new SQLException(e.getMessage(), e);
        } catch (RuntimeException e){
            LOG.error("{dataSource-" + this.getID() + "} init error", e);
            throw e;
        } catch (Error e){
            LOG.error("{dataSource-" + this.getID() + "} init error", e);
            throw e;

        } finally {
            inited = true; //初始化完成后置为true
            lock.unlock(); //释放锁

            if (init && LOG.isInfoEnabled()) {
                String msg = "{dataSource-" + this.getID();

                if (this.name != null && !this.name.isEmpty()) {
                    msg += ",";
                    msg += this.name;
                }

                msg += "} inited";

                LOG.info(msg);
            }
        }
    }

    private void initFromSPIServiceLoader() {
        if (loadSpifilterSkip) { //默认不跳过SPI加载
            return;
        }

        if (autoFilters == null) {
            List filters = new ArrayList();
            ServiceLoader autoFilterLoader = ServiceLoader.load(Filter.class); //加载Filter的实现

            for (Filter filter : autoFilterLoader) {
                AutoLoad autoLoad = filter.getClass().getAnnotation(AutoLoad.class);
                if (autoLoad != null && autoLoad.value()) {
                    filters.add(filter);
                }
            }
            autoFilters = filters;
        }

        for (Filter filter : autoFilters) {
            if (LOG.isInfoEnabled()) {
                LOG.info("load filter from spi :" + filter.getClass().getName());
            }
            addFilter(filter); //把通过SPI机制加载到的filter放到池子的filters里,用于后续责任链触发
        }
    }

    private void initValidConnectionChecker() { //初始化checker
        if (this.validConnectionChecker != null) {
            return;
        }

        String realDriverClassName = driver.getClass().getName(); //根据驱动的class名称,来适配具体的checker实现
        if (JdbcUtils.isMySqlDriver(realDriverClassName)) {
            this.validConnectionChecker = new MySqlValidConnectionChecker(); //假设是mysql类型的驱动,那么适配到mysql的checker,MySqlValidConnectionChecker的构造器参考下面的方法

        } else if (realDriverClassName.equals(JdbcConstants.ORACLE_DRIVER)
                || realDriverClassName.equals(JdbcConstants.ORACLE_DRIVER2)) {
            this.validConnectionChecker = new OracleValidConnectionChecker();

        } else if (realDriverClassName.equals(JdbcConstants.SQL_SERVER_DRIVER)
                || realDriverClassName.equals(JdbcConstants.SQL_SERVER_DRIVER_SQLJDBC4)
                || realDriverClassName.equals(JdbcConstants.SQL_SERVER_DRIVER_JTDS)) {
            this.validConnectionChecker = new MSSQLValidConnectionChecker();

        } else if (realDriverClassName.equals(JdbcConstants.POSTGRESQL_DRIVER)
                || realDriverClassName.equals(JdbcConstants.ENTERPRISEDB_DRIVER)) {
            this.validConnectionChecker = new PGValidConnectionChecker();
        }
    }
    
    //Mysql对应的checker构造器
    public MySqlValidConnectionChecker(){
        try {
            clazz = Utils.loadClass("com.mysql.jdbc.MySQLConnection");
            if (clazz == null) {
                clazz = Utils.loadClass("com.mysql.cj.jdbc.ConnectionImpl");
            }

            if (clazz != null) {
                //如果驱动程序本身有ping方法,则下面的usePingMethod设置为true,后续连接保活测试就会采用ping.invoke的方式触发。
                ping = clazz.getMethod("pingInternal", boolean.class, int.class); 
            }

            if (ping != null) {
                usePingMethod = true;
            }
        } catch (Exception e) {
            LOG.warn("Cannot resolve com.mysql.jdbc.Connection.ping method.  Will use 'SELECT 1' instead.", e);
        }

        configFromProperties(System.getProperties());
    }
posted @ 2019-09-18 06:37  是胖虎捏  阅读(705)  评论(0编辑  收藏  举报