Druid連接池實現(xiàn)分析

主要想要了解幾個問題侠鳄。
1.該連接池是否使用了隊列愚屁,使用的是有界還是無界隊列?
2.拒絕策略是什么樣的絮重?
3.如何被Spring引用的届吁?
4.Spring如何通過該連接池獲取數(shù)據(jù)庫連接的?

項目是Springboot引入druid-spring-boot-starter來使用Druid绿鸣,因此從start開始分析

druid-spring-boot-starter分析

spring.factories

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceAutoConfigure

DruidDataSourceAutoConfigure
該類引入各種配置,看名字暂氯,數(shù)據(jù)庫狀態(tài)的配置一律不管潮模,帶Stat的全部過濾,先關(guān)注下DataSource這個Bean痴施,該Bean在沒有數(shù)據(jù)源時會生效擎厢。返回了一個包裝類DruidDataSourceWrapper

@Configuration
@ConditionalOnClass({DruidDataSource.class})
@AutoConfigureBefore({DataSourceAutoConfiguration.class})
@EnableConfigurationProperties({DruidStatProperties.class, DataSourceProperties.class})
@Import({DruidSpringAopConfiguration.class, DruidStatViewServletConfiguration.class, DruidWebStatFilterConfiguration.class, DruidFilterConfiguration.class})
public class DruidDataSourceAutoConfigure {
    private static final Logger LOGGER = LoggerFactory.getLogger(DruidDataSourceAutoConfigure.class);

    public DruidDataSourceAutoConfigure() {
    }

    @Bean(
        initMethod = "init"
    )
    @ConditionalOnMissingBean
    public DataSource dataSource() {
        LOGGER.info("Init DruidDataSource");
        return new DruidDataSourceWrapper();
    }
}

DruidDataSourceWrapper部分代碼
實現(xiàn)了InitializingBean,設(shè)置了數(shù)據(jù)庫核心屬性辣吃,然后繼承了DruidDataSource动遭,最后增加了許多filter添加方法

public void afterPropertiesSet() throws Exception {
        if (super.getUsername() == null) {
            super.setUsername(this.basicProperties.determineUsername());
        }

        if (super.getPassword() == null) {
            super.setPassword(this.basicProperties.determinePassword());
        }

        if (super.getUrl() == null) {
            super.setUrl(this.basicProperties.determineUrl());
        }

        if (super.getDriverClassName() == null) {
            super.setDriverClassName(this.basicProperties.getDriverClassName());
        }

    }

DruidDataSource
核心類,繼承自DruidAbstractDataSource模板神得,實習(xí)了DruidDataSourceMBean, ManagedDataSource, Referenceable, Closeable, Cloneable, ConnectionPoolDataSource, MBeanRegistration等接口
部分值得關(guān)注的屬性

    private volatile DruidConnectionHolder[] connections;
    private DruidConnectionHolder[] evictConnections;
    private DruidConnectionHolder[] keepAliveConnections;
    private DruidDataSource.CreateConnectionThread createConnectionThread;
    private DruidDataSource.DestroyConnectionThread destroyConnectionThread;
    private LogStatsThread   logStatsThread;
    public static ThreadLocal<Long> waitNanosLocal = new ThreadLocal();
    private final CountDownLatch initedLatch;
    private volatile boolean enable;

    protected static final AtomicLongFieldUpdater<DruidDataSource> recycleErrorCountUpdater = AtomicLongFieldUpdater.newUpdater(DruidDataSource.class, "recycleErrorCount");
    protected static final AtomicLongFieldUpdater<DruidDataSource> connectErrorCountUpdater = AtomicLongFieldUpdater.newUpdater(DruidDataSource.class, "connectErrorCount");
    protected static final AtomicLongFieldUpdater<DruidDataSource> resetCountUpdater = AtomicLongFieldUpdater.newUpdater(DruidDataSource.class, "resetCount");

第一個屬性connections厘惦,必然是數(shù)據(jù)庫連接,沒跑了哩簿,DruidConnectionHolder必然是保存連接的類宵蕉。
DruidConnectionHolder

  protected final DruidAbstractDataSource       dataSource;
  protected final long                          connectionId;
  protected final Connection                    conn;
  protected final List<ConnectionEventListener> connectionEventListeners = new CopyOnWriteArrayList<ConnectionEventListener>();
  protected volatile long                       lastActiveTimeMillis;
  protected volatile long                       lastValidTimeMillis;
  private long                                  useCount                 = 0;
  private long                                  lastNotEmptyWaitNanos;
  protected final boolean                       defaultAutoCommit;
  protected boolean                             discard                  = false;
  public DruidConnectionHolder(DruidAbstractDataSource dataSource, Connection conn, long connectNanoSpan,
                                 Map<String, Object> variables, Map<String, Object> globleVariables)
                                                                                                    throws SQLException{
        this.dataSource = dataSource;
        this.conn = conn;
        this.createNanoSpan = connectNanoSpan;
        this.variables = variables;
        this.globleVariables = globleVariables;

        this.connectTimeMillis = System.currentTimeMillis();
        this.lastActiveTimeMillis = connectTimeMillis;

        this.underlyingAutoCommit = conn.getAutoCommit();

        if (conn instanceof WrapperProxy) {
            this.connectionId = ((WrapperProxy) conn).getId();
        } else {
            this.connectionId = dataSource.createConnectionId();
        }

        {
            boolean initUnderlyHoldability = !holdabilityUnsupported;
            if (JdbcConstants.SYBASE.equals(dataSource.dbType) //
                || JdbcConstants.DB2.equals(dataSource.dbType) //
                || JdbcConstants.HIVE.equals(dataSource.dbType) //
                || JdbcConstants.ODPS.equals(dataSource.dbType) //
            ) {
                initUnderlyHoldability = false;
            }
            if (initUnderlyHoldability) {
                try {
                    this.underlyingHoldability = conn.getHoldability();
                } catch (UnsupportedOperationException e) {
                    holdabilityUnsupported = true;
                    LOG.warn("getHoldability unsupported", e);
                } catch (SQLFeatureNotSupportedException e) {
                    holdabilityUnsupported = true;
                    LOG.warn("getHoldability unsupported", e);
                } catch (SQLException e) {
                    // bug fixed for hive jdbc-driver
                    if ("Method not supported".equals(e.getMessage())) {
                        holdabilityUnsupported = true;
                    }
                    LOG.warn("getHoldability error", e);
                }
            }
        }

        this.underlyingReadOnly = conn.isReadOnly();
        try {
            this.underlyingTransactionIsolation = conn.getTransactionIsolation();
        } catch (SQLException e) {
            // compartible for alibaba corba
            if ("HY000".equals(e.getSQLState())
                    || "com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException".equals(e.getClass().getName())) {
                // skip
            } else {
                throw e;
            }
        }

        this.defaultHoldability = underlyingHoldability;
        this.defaultTransactionIsolation = underlyingTransactionIsolation;
        this.defaultAutoCommit = underlyingAutoCommit;
        this.defaultReadOnly = underlyingReadOnly;
    }

在構(gòu)造方法里面,查詢了下數(shù)據(jù)庫當(dāng)前的隔離級別节榜,設(shè)置了connectionId羡玛,autoCommit,connectTimeMillis宗苍,基本上沒啥需要特殊注意的稼稿。返回繼續(xù)看DruidDataSource類。

    private volatile DruidConnectionHolder[] connections;
    private DruidConnectionHolder[]          evictConnections;
    private DruidConnectionHolder[]          keepAliveConnections;

connections連接的總數(shù)讳窟,evictConnections回收的連接让歼,keepAliveConnections活躍的總數(shù)

接下來看下DruidDataSource的初始化方法,一共200多行挪钓,還是比較長的

public void init() throws SQLException {
        if (inited) {
            return;
        }

        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();
            if (this.id > 1) {
                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();
            }

            for (Filter filter : filters) {
                filter.init(this);
            }

            if (this.dbType == null || this.dbType.length() == 0) {
                this.dbType = JdbcUtils.getDbType(jdbcUrl, null);
            }

            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();
            }

            initFromSPIServiceLoader();

            if (this.driver == null) {
                if (this.driverClass == null || this.driverClass.isEmpty()) {
                    this.driverClass = JdbcUtils.getDriverClassName(this.jdbcUrl);
                }

                if (MockDriver.class.getName().equals(driverClass)) {
                    driver = MockDriver.instance;
                } else {
                    driver = JdbcUtils.createDriver(driverClassLoader, driverClass);
                }
            } else {
                if (this.driverClass == null) {
                    this.driverClass = driver.getClass().getName();
                }
            }

            initCheck();

            initExceptionSorter();
            initValidConnectionChecker();
            validationQueryCheck();

            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);

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

            SQLException connectError = null;

            if (createScheduler != null) {
                for (int i = 0; i < initialSize; ++i) {
                    createTaskCount++;
                    CreateConnectionTask task = new CreateConnectionTask(true);
                    this.createSchedulerFuture = createScheduler.submit(task);
                }
            } else if (!asyncInit) {
                try {
                    // init connections
                    for (int i = 0; i < initialSize; ++i) {
                        PhysicalConnectionInfo pyConnectInfo = createPhysicalConnection();
                        DruidConnectionHolder holder = new DruidConnectionHolder(this, pyConnectInfo);
                        connections[poolingCount] = holder;
                        incrementPoolingCount();
                    }

                    if (poolingCount > 0) {
                        poolingPeak = poolingCount;
                        poolingPeakTime = System.currentTimeMillis();
                    }
                } catch (SQLException ex) {
                    LOG.error("init datasource error, url: " + this.getUrl(), ex);
                    connectError = ex;
                }
            }

            createAndLogThread();
            createAndStartCreatorThread();
            createAndStartDestroyThread();

            initedLatch.await();
            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();
                }
            }

        } 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;
            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);
            }
        }
    }

init方法會初始化id為1的連接池是越,核心poolingCount 默認(rèn)為2,connections碌上、evictConnections 倚评、keepAliveConnections 的大小為maxActive的值浦徊。然后創(chuàng)建CreateConnectionThread 、LogStatsThread天梧、DestroyConnectionThread三個線程盔性,都是以守護(hù)線程的方式啟動的。

PhysicalConnectionInfo pyConnectInfo = this.createPhysicalConnection();

會創(chuàng)建一個條數(shù)據(jù)庫連接
可以觀察PERFORMANCE_SCHEMA.threads
會發(fā)現(xiàn)新增了兩條記錄


image.png

再觀察一下java線程池情況

java -classpath "sa-jdi.jar" sun.jvm.hotspot.HSDB

使用HSDB查看創(chuàng)建情況


image.png

CreateConnectionThread

          while(true) {
                PhysicalConnectionInfo connection;
                label338: {
                    while(true) {
                        try {
                            DruidDataSource.this.lock.lockInterruptibly();
                        } catch (InterruptedException var33) {
                            break;
                        }

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

                        try {
                            boolean emptyWait = true;
                            if (DruidDataSource.this.createError != null && DruidDataSource.this.poolingCount == 0 && !discardChanged) {
                                emptyWait = false;
                            }

                            if (emptyWait && DruidDataSource.this.asyncInit && DruidDataSource.this.createCount < (long)DruidDataSource.this.initialSize) {
                                emptyWait = false;
                            }

                            if (emptyWait) {
                                if (DruidDataSource.this.poolingCount >= DruidDataSource.this.notEmptyWaitThreadCount && (!DruidDataSource.this.keepAlive || DruidDataSource.this.activeCount + DruidDataSource.this.poolingCount >= DruidDataSource.this.minIdle)) {
                                    DruidDataSource.this.empty.await();
                                }

                                if (DruidDataSource.this.activeCount + DruidDataSource.this.poolingCount >= DruidDataSource.this.maxActive) {
                                    DruidDataSource.this.empty.await();
                                    continue;
                                }
                            }
                        } catch (InterruptedException var31) {
                            DruidDataSource.this.lastCreateError = var31;
                            DruidDataSource.this.lastErrorTimeMillis = System.currentTimeMillis();
                            if (!DruidDataSource.this.closing) {
                                DruidDataSource.LOG.error("create connection Thread Interrupted, url: " + DruidDataSource.this.jdbcUrl, var31);
                            }
                            break;
                        } finally {
                            DruidDataSource.this.lock.unlock();
                        }

                        connection = null;

                        try {
                            connection = DruidDataSource.this.createPhysicalConnection();
                            break label338;
                        } catch (SQLException var28) {
                            DruidDataSource.LOG.error("create connection SQLException, url: " + DruidDataSource.this.jdbcUrl + ", errorCode " + var28.getErrorCode() + ", state " + var28.getSQLState(), var28);
                            ++errorCount;
                            if (errorCount <= DruidDataSource.this.connectionErrorRetryAttempts || DruidDataSource.this.timeBetweenConnectErrorMillis <= 0L) {
                                break label338;
                            }

                            DruidDataSource.this.setFailContinuous(true);
                            if (DruidDataSource.this.failFast) {
                                DruidDataSource.this.lock.lock();

                                try {
                                    DruidDataSource.this.notEmpty.signalAll();
                                } finally {
                                    DruidDataSource.this.lock.unlock();
                                }
                            }

                            if (!DruidDataSource.this.breakAfterAcquireFailure) {
                                try {
                                    Thread.sleep(DruidDataSource.this.timeBetweenConnectErrorMillis);
                                    break label338;
                                } catch (InterruptedException var27) {
                                }
                            }
                            break;
                        } catch (RuntimeException var29) {
                            DruidDataSource.LOG.error("create connection RuntimeException", var29);
                            DruidDataSource.this.setFailContinuous(true);
                        } catch (Error var30) {
                            DruidDataSource.LOG.error("create connection Error", var30);
                            DruidDataSource.this.setFailContinuous(true);
                            break;
                        }
                    }

                    return;
                }

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

                    errorCount = 0;
                }
            }

該線程負(fù)責(zé)創(chuàng)建數(shù)據(jù)庫連接呢岗,如果有線程在等待連接的話
DestroyConnectionThread

        public void run() {
            initedLatch.countDown();

            for (;;) {
                // 從前面開始刪除
                try {
                    if (closed) {
                        break;
                    }

                    if (timeBetweenEvictionRunsMillis > 0) {
                        Thread.sleep(timeBetweenEvictionRunsMillis);
                    } else {
                        Thread.sleep(1000); //
                    }

                    if (Thread.interrupted()) {
                        break;
                    }

                    destroyTask.run();
                } catch (InterruptedException e) {
                    break;
                }
            }
        }
  public class DestroyTask implements Runnable {

        @Override
        public void run() {
            shrink(true, keepAlive);

            if (isRemoveAbandoned()) {
                removeAbandoned();
            }
        }

    }
public void shrink(boolean checkTime, boolean keepAlive) {
    // 回收超過一定時間不活躍
}

持有連接數(shù)=核心連接數(shù)+活躍的連接數(shù)

int allCount = this.poolingCount + this.activeCount;

至此冕香,Druid連接池初始化基本上有了大致的了解,下面從獲取連接的方法入手后豫,查看下線程獲取數(shù)據(jù)庫連接的過程悉尾。
DruidPooledConnection

    public DruidPooledConnection getConnectionDirect(long maxWaitMillis) throws SQLException {
        int notFullTimeoutRetryCnt = 0;

        DruidPooledConnection poolableConnection;
        while(true) {
            while(true) {
                try {
                    poolableConnection = this.getConnectionInternal(maxWaitMillis);
                    break;
                } catch (GetConnectionTimeoutException var19) {
                    // 超時獲取不到poolableConnection,異常處理
                }
            }

            if (this.testOnBorrow) {
                // 調(diào)用ping方法測試連接是否可用
                boolean validate = this.testConnectionInternal(poolableConnection.holder, poolableConnection.conn);
                if (validate) {
                    break;
                }

                if (LOG.isDebugEnabled()) {
                    LOG.debug("skip not validate connection.");
                }

                Connection realConnection = poolableConnection.conn;
                this.discardConnection(realConnection);
            } else {
                Connection realConnection = poolableConnection.conn;
                if (poolableConnection.conn.isClosed()) {
                    this.discardConnection((Connection)null);
                } else {
                    if (!this.testWhileIdle) {
                        break;
                    }

                    long currentTimeMillis = System.currentTimeMillis();
                    long lastActiveTimeMillis = poolableConnection.holder.lastActiveTimeMillis;
                    long idleMillis = currentTimeMillis - lastActiveTimeMillis;
                    long timeBetweenEvictionRunsMillis = this.timeBetweenEvictionRunsMillis;
                    if (timeBetweenEvictionRunsMillis <= 0L) {
                        timeBetweenEvictionRunsMillis = 60000L;
                    }

                    if (idleMillis < timeBetweenEvictionRunsMillis && idleMillis >= 0L) {
                        break;
                    }

                    boolean validate = this.testConnectionInternal(poolableConnection.holder, poolableConnection.conn);
                    if (validate) {
                        break;
                    }

                    if (LOG.isDebugEnabled()) {
                        LOG.debug("skip not validate connection.");
                    }

                    this.discardConnection(realConnection);
                }
            }
        }

        if (this.removeAbandoned) {
            StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
            poolableConnection.connectStackTrace = stackTrace;
            poolableConnection.setConnectedTimeNano();
            poolableConnection.traceEnable = true;
            this.activeConnectionLock.lock();

            try {
                this.activeConnections.put(poolableConnection, PRESENT);
            } finally {
                this.activeConnectionLock.unlock();
            }
        }

        if (!this.defaultAutoCommit) {
            poolableConnection.setAutoCommit(false);
        }

        return poolableConnection;
    }

通過調(diào)用poolableConnection = this.getConnectionInternal(maxWaitMillis);獲取連接

    private DruidPooledConnection getConnectionInternal(long maxWait) throws SQLException {
        if (this.closed) {
            connectErrorCountUpdater.incrementAndGet(this);
            throw new DataSourceClosedException("dataSource already closed at " + new Date(this.closeTimeMillis));
        } else if (!this.enable) {
            connectErrorCountUpdater.incrementAndGet(this);
            throw new DataSourceDisableException();
        } else {
            long nanos = TimeUnit.MILLISECONDS.toNanos(maxWait);
            int maxWaitThreadCount = this.maxWaitThreadCount;
            boolean createDirect = false;

            DruidConnectionHolder holder;
            while(true) {
                if (createDirect) {
                        //創(chuàng)建新的連接挫酿,代碼略過
                        this.lock.lock();

                        // 更新狀態(tài)字段构眯,代碼略過
                    }
                }

                // 中斷方式獲取鎖,代碼略過

                try {
                    if (最大等待線程數(shù)是否超出) {
                        connectErrorCountUpdater.incrementAndGet(this);
                        throw new SQLException("maxWaitThreadCount " + maxWaitThreadCount + ", current wait Thread count " + this.lock.getQueueLength());
                    }

                    if (失敗次數(shù)是否超出最大失敗次數(shù)) {
                        // 輸出日志早龟,代碼略過

                        throw new SQLException(errorMsg, this.lastFatalError);
                    }

                    ++this.connectCount;
                    if (this.createScheduler != null && this.poolingCount == 0 && this.activeCount < this.maxActive && creatingCountUpdater.get(this) == 0 && this.createScheduler instanceof ScheduledThreadPoolExecutor) {
                        // 該條件不會被滿足惫霸,因為this.createScheduler默認(rèn)為空
                        ScheduledThreadPoolExecutor executor = (ScheduledThreadPoolExecutor)this.createScheduler;
                        if (executor.getQueue().size() > 0) {
                            createDirect = true;
                            continue;
                        }
                    }
                    // 獲取是否有超時時間,這里會轉(zhuǎn)換成納秒
                    if (maxWait > 0L) {
                        holder = this.pollLast(nanos);
                    } else {
                        holder = this.takeLast();
                    }

                    if (holder != null) {
                        ++this.activeCount;
                        if (this.activeCount > this.activePeak) {
                            this.activePeak = this.activeCount;
                            this.activePeakTime = System.currentTimeMillis();
                        }
                    }
                    break;
                } catch (InterruptedException var24) {
                    connectErrorCountUpdater.incrementAndGet(this);
                    throw new SQLException(var24.getMessage(), var24);
                } catch (SQLException var25) {
                    connectErrorCountUpdater.incrementAndGet(this);
                    throw var25;
                } finally {
                    this.lock.unlock();
                }
            }
            if (holder == null) {
            // 如果holder為空的葱弟,異常處理壹店,這里略過
            } else {
                holder.incrementUseCount();
                DruidPooledConnection poolalbeConnection = new DruidPooledConnection(holder);
                return poolalbeConnection;
            }
        }
    }

holder = this.pollLast(nanos); 會調(diào)用這個獲取DruidConnectionHolder

    private DruidConnectionHolder pollLast(long nanos) throws InterruptedException, SQLException {
        long estimate = nanos;

        while(this.poolingCount == 0) {
            this.emptySignal();
            if (this.failFast && this.failContinuous.get()) {
                throw new DataSourceNotAvailableException(this.createError);
            }

            if (estimate <= 0L) {
                waitNanosLocal.set(nanos - estimate);
                return null;
            }

            ++this.notEmptyWaitThreadCount;
            if (this.notEmptyWaitThreadCount > this.notEmptyWaitThreadPeak) {
                this.notEmptyWaitThreadPeak = this.notEmptyWaitThreadCount;
            }

            try {
                long startEstimate = estimate;
                estimate = this.notEmpty.awaitNanos(estimate);
                ++this.notEmptyWaitCount;
                this.notEmptyWaitNanos += startEstimate - estimate;
                if (!this.enable) {
                    connectErrorCountUpdater.incrementAndGet(this);
                    throw new DataSourceDisableException();
                }
            } catch (InterruptedException var10) {
                this.notEmpty.signal();
                ++this.notEmptySignalCount;
                throw var10;
            } finally {
                --this.notEmptyWaitThreadCount;
            }

            if (this.poolingCount != 0) {
                break;
            }

            if (estimate <= 0L) {
                waitNanosLocal.set(nanos - estimate);
                return null;
            }
        }

        this.decrementPoolingCount();
        DruidConnectionHolder last = this.connections[this.poolingCount];
        this.connections[this.poolingCount] = null;
        long waitNanos = nanos - estimate;
        last.setLastNotEmptyWaitNanos(waitNanos);
        return last;
    }

自旋獲取連接,這個地方可用發(fā)現(xiàn)并沒有等待隊列芝加。
再來看看連接數(shù)情況
初始化完成連接情況硅卢,兩個sleep的連接


初始化完成

這里打隔點,模擬有三個線程需要同時獲取連接的情況藏杖,可以發(fā)現(xiàn)老赤,前面兩個線程獲取到連接后,第三個線程獲取的時候會先超時獲取不到制市,等待CreateConnectionThread 創(chuàng)建了新的連接后返回抬旺。


image.png

總結(jié),閱讀到這里開頭的四個問題都有答案了祥楣。
1.該連接池是否使用了隊列开财,使用的是有界還是無界隊列?
不使用隊列误褪,就是忙等待责鳍,while循環(huán)。但是超時會返回兽间。
2.拒絕策略是什么樣的历葛?
超過了最大等待線程數(shù)就會直接拋出SQLException(前提設(shè)置了maxWaitThreadCount)
3.如何被Spring引用的?
創(chuàng)建了一個DataSource Bean,使用DruidDataSourceWrapper包裝類恤溶。
4.Spring如何通過該連接池獲取數(shù)據(jù)庫連接的乓诽?
如果有可以用的連接,直接返回咒程,否則忙等待創(chuàng)建線程創(chuàng)建新的連接鸠天,獲取到連接返回,獲取不到則超時處理帐姻。

補充問題:
用完了連接稠集,是如何將連接歸還給DruidDataSource的?
JPA的話會調(diào)用
NonContextualJdbcConnectionAccess

  public void releaseConnection(Connection connection) throws SQLException {
        try {
            this.listener.jdbcConnectionReleaseStart();
            this.connectionProvider.closeConnection(connection);
        } finally {
            this.listener.jdbcConnectionReleaseEnd();
        }

    }

this.connectionProvider.dataSource=DruidDataSourceWrapper(最開始定義的bean)
DatasourceConnectionProviderImpl

    public void closeConnection(Connection connection) throws SQLException {
        connection.close();
    }

DruidPooledConnection

   public void close() throws SQLException {
        if (!this.disable) {
            DruidConnectionHolder holder = this.holder;
            if (holder == null) {
                if (this.dupCloseLogEnable) {
                    LOG.error("dup close");
                }

            } else {
                DruidAbstractDataSource dataSource = holder.getDataSource();
                boolean isSameThread = this.getOwnerThread() == Thread.currentThread();
                if (!isSameThread) {
                    dataSource.setAsyncCloseConnectionEnable(true);
                }

                if (dataSource.isAsyncCloseConnectionEnable()) {
                    this.syncClose();
                } else {
                    Iterator var4 = holder.getConnectionEventListeners().iterator();

                    while(var4.hasNext()) {
                        ConnectionEventListener listener = (ConnectionEventListener)var4.next();
                        listener.connectionClosed(new ConnectionEvent(this));
                    }

                    List<Filter> filters = dataSource.getProxyFilters();
                    if (filters.size() > 0) {
                        FilterChainImpl filterChain = new FilterChainImpl(dataSource);
                        filterChain.dataSource_recycle(this);
                    } else {
                        this.recycle();
                    }

                    this.disable = true;
                }
            }
        }
    }

經(jīng)過一系列方法饥瓷,最終會調(diào)用DruidDataSource的recycle方法回收資源
DruidDataSource

    protected void recycle(DruidPooledConnection pooledConnection) throws SQLException {
        // 回收連接資源
    }
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末剥纷,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子呢铆,更是在濱河造成了極大的恐慌筷畦,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,941評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異吼砂,居然都是意外死亡因俐,警方通過查閱死者的電腦和手機蓉坎,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,397評論 3 395
  • 文/潘曉璐 我一進(jìn)店門钳踊,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人祭埂,你說我怎么就攤上這事舌界。” “怎么了?”我有些...
    開封第一講書人閱讀 165,345評論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長炕泳,這世上最難降的妖魔是什么登刺? 我笑而不...
    開封第一講書人閱讀 58,851評論 1 295
  • 正文 為了忘掉前任皇耗,我火速辦了婚禮廊宪,結(jié)果婚禮上箭启,老公的妹妹穿的比我還像新娘傅寡。我一直安慰自己芜抒,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,868評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上伏蚊,一...
    開封第一講書人閱讀 51,688評論 1 305
  • 那天匀借,我揣著相機與錄音瑰艘,去河邊找鬼芒率。 笑死充择,一個胖子當(dāng)著我的面吹牛德玫,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播椎麦,決...
    沈念sama閱讀 40,414評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼宰僧,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了观挎?” 一聲冷哼從身側(cè)響起琴儿,我...
    開封第一講書人閱讀 39,319評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎键兜,沒想到半個月后凤类,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,775評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡普气,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,945評論 3 336
  • 正文 我和宋清朗相戀三年谜疤,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片现诀。...
    茶點故事閱讀 40,096評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡夷磕,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出仔沿,到底是詐尸還是另有隱情坐桩,我是刑警寧澤,帶...
    沈念sama閱讀 35,789評論 5 346
  • 正文 年R本政府宣布封锉,位于F島的核電站绵跷,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏成福。R本人自食惡果不足惜碾局,卻給世界環(huán)境...
    茶點故事閱讀 41,437評論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望奴艾。 院中可真熱鬧净当,春花似錦、人聲如沸蕴潦。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,993評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽潭苞。三九已至忽冻,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間此疹,已是汗流浹背甚颂。 一陣腳步聲響...
    開封第一講書人閱讀 33,107評論 1 271
  • 我被黑心中介騙來泰國打工蜜猾, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人振诬。 一個月前我還...
    沈念sama閱讀 48,308評論 3 372
  • 正文 我出身青樓蹭睡,卻偏偏與公主長得像,于是被迫代替她去往敵國和親赶么。 傳聞我的和親對象是個殘疾皇子肩豁,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,037評論 2 355

推薦閱讀更多精彩內(nèi)容