• HikariCP源码阅读笔记


    加入HikariCP的maven依赖

    1. <dependency>
    2. <groupId>com.zaxxergroupId>
    3. <artifactId>HikariCPartifactId>
    4. <version>4.0.3version>
    5. dependency>
    6. <dependency>
    7. <groupId>mysqlgroupId>
    8. <artifactId>mysql-connector-javaartifactId>
    9. <version>8.0.15version>
    10. dependency>
    11. <dependency>
    12. <groupId>ch.qos.logbackgroupId>
    13. <artifactId>logback-coreartifactId>
    14. <version>1.2.3version>
    15. <type>jartype>
    16. dependency>
    17. <dependency>
    18. <groupId>ch.qos.logbackgroupId>
    19. <artifactId>logback-classicartifactId>
    20. <version>1.2.3version>
    21. <type>jartype>
    22. dependency>

    测试代码

    1. public static void main(String[] args) throws SQLException {
    2. Properties properties = new Properties();
    3. properties.setProperty("jdbcUrl", jdbcUrl);
    4. properties.setProperty("username", username);
    5. properties.setProperty("password", password);
    6. properties.setProperty("driverClassName", driverClassName);
    7. HikariConfig config = new HikariConfig(properties);
    8. HikariDataSource dataSource = new HikariDataSource(config);
    9. Connection connection = dataSource.getConnection();
    10. String sql = "select * from table";
    11. PreparedStatement preparedStatement = connection.prepareStatement(sql);
    12. ResultSet set = preparedStatement.executeQuery();
    13. while (set.next()) {
    14. ……
    15. }
    16. connection.close();
    17. dataSource.close();
    18. }

    在执行的过程中,测试代码核心主要只有这几行

    1. HikariConfig config = new HikariConfig(properties);
    2. HikariDataSource dataSource = new HikariDataSource(config);
    3. Connection connection = dataSource.getConnection();
    4. connection.close();
    5. dataSource.close();

    从测试代码中可将源码阅读流程主要分以下几步:

    1. 加载配置
    2. 通过配置创建连接池
    3. 从连接池里获取连接
    4. 关闭连接
    5. 关闭连接池

    下面分别对这五步进行介绍

    一. 加载配置

    HikariConfig config = new HikariConfig(properties);

    流程如下:

    1. 将Properties对象作为参数通过HikariConfig的构造方法传入进这个类中
    2. 在HikariConfig类初始化的过程中就完成了部分属性的加载和设置。
    3. 完成对HikariConfig类的部分属性的初次赋值后,执行传入参数Properties的动态加载处理
      1. Properties对象的映射关系中,key对应的是HikariConfig的属性
      2. 根据反射,对HikariConfig的属性通过set方法,把Properties对应的value值进行设置
      3. 如果能匹配上,就相当于进行了第二次的赋值。
    4. 最终得到一个带有属性的HikariConfig对象。

    其他说明:配置的来源为自定义的Properties类、指定的Properties文件路径、系统环境变量hikaricp.configurationFile所指向的Poperties文件路径。


    二. 通过配置创建连接池

    HikariDataSource dataSource = new HikariDataSource(config);

    流程图如下:

     HikariDataSource类是HikariConfig类的子类,它内部定义了如下属性,主要是一个不会再被修改的fastrPathPool,和一个内部一致性的pool。

    1. public class HikariDataSource extends HikariConfig implements DataSource, Closeable{
    2. private static final Logger LOGGER = LoggerFactory.getLogger(HikariDataSource.class);
    3. private final AtomicBoolean isShutdown = new AtomicBoolean();
    4. private final HikariPool fastPathPool;
    5. private volatile HikariPool pool;
    6. public HikariDataSource(){
    7. super();
    8. fastPathPool = null;
    9. }
    10. public HikariDataSource(HikariConfig configuration){
    11. configuration.validate();
    12. configuration.copyStateTo(this);
    13. LOGGER.info("{} - Starting...", configuration.getPoolName());
    14. pool = fastPathPool = new HikariPool(this);
    15. LOGGER.info("{} - Start completed.", configuration.getPoolName());
    16. this.seal();
    17. }
    18. }

    HikariDataSource有两个构造方法:

    一个是无参的, 如果使用它进行HikariDataSource的类对象创建,它会通过super方法,访问HikariConfig类的无参方法,进行HikariConfig类的初始化和部分属性加载。

    一个是有参的,接收一个HikariConfig类,相当于作为父类的HikariyConfig提前完成了初始化和属性分配,然后传到了子类HikariDataSource里。从构造方法里可以简单的看到执行流程主要分为以下四步:

    1.执行configuration.validate()

    • 如果当前连接池还没有poolName,那就自动创建一个poolName
    • 对加载到的配置参数进行校验,例如jdbcUrl,driverClassName等
    • 对数字类的配置参数进行校验和有界处理,例如maxPoolSize,没指定时默认是10;minIdle如果没指定或者指定不合规矩时,就等于maxPoolSize,等等。

    2.执行configuration.copyStateTo(this)

    • 把传来的参数configuration,也就是HikariConfig对象的属性都给当前的HikariDataSource对象赋值一份,相当于本身继承父类的属性原本为null,现在都被赋值了。
    • 设置HikariDataSource的sealed值等于false

    3. pool = fastPathPool = new HikariPool(this);

    我们发现HikariPool类继承了PoolBase类,实现了接口HikariPoolMXBean和接口IBagStateListener。

    所以在通过构造方法获得HikariPool对象前,会先初始化PoolBase类和HikariPool类。

    在PoolBase类里,因为它对外的构造方法必须是有参的,所以在初始化的时候,PoolBase类只将相关属性进行了初始化,同时因为在该类内部有多个静态类、接口等,这些也要完成初始化。


    PoolBase类下的静态类、接口等

    1. static class ConnectionSetupException extends Exception
    2. private static class SynchronousExecutor implements Executor{
    3. @Override
    4. public void execute(Runnable command){
    5. try {
    6. command.run();
    7. } catch (Exception t) {
    8. LoggerFactory.getLogger(PoolBase.class).debug("Failed to execute: {}", command, t);
    9. }
    10. }
    11. }
    12. interface IMetricsTrackerDelegate extends AutoCloseable
    13. static class MetricsTrackerDelegate implements IMetricsTrackerDelegate
    14. static final class NopMetricsTrackerDelegate implements IMetricsTrackerDelegate

    HikariPool类下的静态类、接口等

    1. private final class PoolEntryCreator implements Callable{
    2. // PoolEntry对象的创建类线程
    3. }
    4. private final class HouseKeeper implements Runnable{
    5. // …… 定时任务执行的线程
    6. }
    7. private final class MaxLifetimeTask implements Runnable{
    8. ……
    9. }
    10. private final class KeepaliveTask implements Runnable{
    11. ……
    12. }
    13. public static class PoolInitializationException extends RuntimeException{
    14. ……
    15. }

    构造方法

    在PoolBase类和HikariPool类完成初始化后,HikariPool开始了构造方法的执行。

    1. public HikariPool(final HikariConfig config){
    2. super(config);
    3. this.connectionBag = new ConcurrentBag<>(this);
    4. this.suspendResumeLock = config.isAllowPoolSuspension() ? new SuspendResumeLock() : SuspendResumeLock.FAUX_LOCK;
    5. this.houseKeepingExecutorService = initializeHouseKeepingExecutorService();
    6. checkFailFast();
    7. if (config.getMetricsTrackerFactory() != null) {
    8. setMetricsTrackerFactory(config.getMetricsTrackerFactory());
    9. }else {
    10. setMetricRegistry(config.getMetricRegistry());
    11. }
    12. setHealthCheckRegistry(config.getHealthCheckRegistry());
    13. handleMBeans(this, true);
    14. ThreadFactory threadFactory = config.getThreadFactory();
    15. final int maxPoolSize = config.getMaximumPoolSize();
    16. LinkedBlockingQueue addConnectionQueue = new LinkedBlockingQueue<>(maxPoolSize);
    17. this.addConnectionQueueReadOnlyView = unmodifiableCollection(addConnectionQueue);
    18. this.addConnectionExecutor = createThreadPoolExecutor(addConnectionQueue, poolName + " connection adder", threadFactory, new ThreadPoolExecutor.DiscardOldestPolicy());
    19. this.closeConnectionExecutor = createThreadPoolExecutor(maxPoolSize, poolName + " connection closer", threadFactory, new ThreadPoolExecutor.CallerRunsPolicy());
    20. this.leakTaskFactory = new ProxyLeakTaskFactory(config.getLeakDetectionThreshold(), houseKeepingExecutorService);
    21. this.houseKeeperTask = houseKeepingExecutorService.scheduleWithFixedDelay(new HouseKeeper(), 100L, housekeepingPeriodMs, MILLISECONDS);
    22. if (Boolean.getBoolean("com.zaxxer.hikari.blockUntilFilled") && config.getInitializationFailTimeout() > 1) {
    23. addConnectionExecutor.setMaximumPoolSize(Math.min(16, Runtime.getRuntime().availableProcessors()));
    24. addConnectionExecutor.setCorePoolSize(Math.min(16, Runtime.getRuntime().availableProcessors()));
    25. final long startTime = currentTime();
    26. while (elapsedMillis(startTime) < config.getInitializationFailTimeout() && getTotalConnections() < config.getMinimumIdle()) {
    27. quietlySleep(MILLISECONDS.toMillis(100));
    28. }
    29. addConnectionExecutor.setCorePoolSize(1);
    30. addConnectionExecutor.setMaximumPoolSize(1);
    31. }
    32. }

     
    通过super(config)方法

    访问PoolBase的有参构造方法,将当前HikariDataSource作为一个HikariConfig传到PoolBase里。

    1. PoolBase(final HikariConfig config){
    2. this.config = config;
    3. this.networkTimeout = UNINITIALIZED;
    4. this.catalog = config.getCatalog();
    5. this.schema = config.getSchema();
    6. this.isReadOnly = config.isReadOnly();
    7. this.isAutoCommit = config.isAutoCommit();
    8. this.exceptionOverride = UtilityElf.createInstance(config.getExceptionOverrideClassName(), SQLExceptionOverride.class);
    9. this.transactionIsolation = UtilityElf.getTransactionIsolation(config.getTransactionIsolation());
    10. this.isQueryTimeoutSupported = UNINITIALIZED;
    11. this.isNetworkTimeoutSupported = UNINITIALIZED;
    12. this.isUseJdbc4Validation = config.getConnectionTestQuery() == null;
    13. this.isIsolateInternalQueries = config.isIsolateInternalQueries();
    14. this.poolName = config.getPoolName();
    15. this.connectionTimeout = config.getConnectionTimeout();
    16. this.validationTimeout = config.getValidationTimeout();
    17. this.lastConnectionFailure = new AtomicReference<>();
    18. initializeDataSource();
    19. }


    在PoolBase类里完成了对PoolBase类里的部分属性字段的赋值工作。完毕后执行了initializeDataSource()方法。

    1. private void initializeDataSource(){
    2. final String jdbcUrl = config.getJdbcUrl();
    3. final String username = config.getUsername();
    4. final String password = config.getPassword();
    5. final String dsClassName = config.getDataSourceClassName();
    6. final String driverClassName = config.getDriverClassName();
    7. final String dataSourceJNDI = config.getDataSourceJNDI();
    8. final Properties dataSourceProperties = config.getDataSourceProperties();
    9. DataSource ds = config.getDataSource();
    10. if (dsClassName != null && ds == null) {
    11. ds = createInstance(dsClassName, DataSource.class);
    12. PropertyElf.setTargetFromProperties(ds, dataSourceProperties);
    13. } else if (jdbcUrl != null && ds == null) {
    14. ds = new DriverDataSource(jdbcUrl, driverClassName, dataSourceProperties, username, password);
    15. } else if (dataSourceJNDI != null && ds == null) {
    16. try {
    17. InitialContext ic = new InitialContext();
    18. ds = (DataSource) ic.lookup(dataSourceJNDI);
    19. } catch (NamingException e) {
    20. throw new PoolInitializationException(e);
    21. }
    22. }
    23. if (ds != null) {
    24. setLoginTimeout(ds);
    25. createNetworkTimeoutExecutor(ds, dsClassName, jdbcUrl);
    26. }
    27. this.dataSource = ds;
    28. }


    因为一般不会设定DataSourceClassName,所以最终会通过DriverDataSource类的构造方法创建一个DataSource对象ds。

    ds = new DriverDataSource(jdbcUrl, driverClassName, dataSourceProperties, username, password);

    该构造方法的作用主要是完成了对DriverDataSource类的属性的赋值操作,同时对jdbcUrl进行了格式验证。如果格式不正确就会抛出异常。

    1. public final class DriverDataSource implements DataSource{
    2. private static final Logger LOGGER = LoggerFactory.getLogger(DriverDataSource.class);
    3. private static final String PASSWORD = "password";
    4. private static final String USER = "user";
    5. private final String jdbcUrl;
    6. private final Properties driverProperties;
    7. private Driver driver;
    8. ……
    9. ……
    10. }

    得到的这个ds对象会进行login超时时间设置,为当前配置的connectionTimeout。

    因为当前连接的是mysql数据库,所会设置当前PoolBase里的netTimeoutExecutor属性为new SynchronousExecutor对象,非Mysql时会创建一个ThreadPoolExecutor对象作为这个netTimeExecutor。

    设置当前PoolBase里的dataSource属性为当前的ds。

    设置HikariPool里的connectionBag属性。

    因为HikariPool也实现了IBagStateListener接口,所以通过ConcurrentBag的构造方法创建此connectionBag对象时,将HikariPool作为参数进行传递。

    1. public class ConcurrentBagextends IConcurrentBagEntry> implements AutoCloseable{
    2. private static final Logger LOGGER = LoggerFactory.getLogger(ConcurrentBag.class);
    3. private final CopyOnWriteArrayList sharedList;
    4. private final boolean weakThreadLocals;
    5. private final ThreadLocal> threadList;
    6. private final IBagStateListener listener;
    7. private final AtomicInteger waiters;
    8. private volatile boolean closed;
    9. private final SynchronousQueue handoffQueue;
    10. public ConcurrentBag(final IBagStateListener listener){
    11. this.listener = listener;
    12. this.weakThreadLocals = useWeakThreadLocals();
    13. this.handoffQueue = new SynchronousQueue<>(true);
    14. this.waiters = new AtomicInteger();
    15. this.sharedList = new CopyOnWriteArrayList<>();
    16. if (weakThreadLocals) {
    17. this.threadList = ThreadLocal.withInitial(() -> new ArrayList<>(16));
    18. } else {
    19. this.threadList = ThreadLocal.withInitial(() -> new FastList<>(IConcurrentBagEntry.class, 16));
    20. }
    21. }
    22. }

     构造方法完成了对ConncurentBag对象属性的赋值,threadList最终使用的是自定义的FastList。

    设置SuspendResumeLock

    因为config的isAllowPoolSuspension默认是false,所以对于HikariPool的suspendResumeLock属性,默认使用了空的SuspendResumeLock对象,即它的方法都没有具体实现。如果isAllowPoolSuspension是true,会通过SuspendResumeLock构造方法创建对象,里面会使用到Semaphore类创建一个公平的信号量对象,最大容量是10000。

    设置ScheduledExecutorService类型的houseKeepingExecutorService。

    因为当前的config并没有配置ScheduledExecutor对象,所以就会默认实现一个corePoolSize等于1的ScheduledThreadExecutor对象作为houseKeepingExecutorService.

    执行checkFailFast方法:

    1. 如果配置了initiallizationFailTimeout小于0,跳过该方法。没配置使用默认的1,就会执行该方法。
    2. 访问HikariPool的createPoolEntry方法,然后跳到父类PoolBase的newPoolEntry方法里。
    3. 该newPoolEntry方法会访问newConnection方法,进行一次数据库的连接操作。
    4. 连接成功会得到一个Connection对象,并为此对象设置原始的属性。连接失败则会记录此次失败的异常,并抛出此异常。
    5. 创建成功的Connection对象则会和其他属性一起创建一个PoolEntry对象。
    6. 同时在HikariPool的createPoolEntry方法里为PoolEntry再次设置属性。并将此PoolEntry对象加入到connectionBag对象的sharedList中。
    7. 遇到异常,会抛出来,中断后续所有流程。
    1. PoolEntry(final Connection connection, final PoolBase pool, final boolean isReadOnly, final boolean isAutoCommit){
    2. this.connection = connection;
    3. this.hikariPool = (HikariPool) pool;
    4. this.isReadOnly = isReadOnly;
    5. this.isAutoCommit = isAutoCommit;
    6. this.lastAccessed = currentTime();
    7. this.openStatements = new FastList<>(Statement.class, 16);
    8. }

    以当前已设置好的maxPoolSize作为长度,创建一个LinkedBlockingQueue,

    同时根据此队列,分别设置HikariPool的属性:

    • addConnectionQueueReadOnlyView 是一个UnmodifiableCollection的对象。
    • addConnectionExecutor是一个核心和最大线程数都等于1的线程池。
    • closeConnectionExecutor是一个核心和最大线程数都等于1的线程池。

    后续完成HikariPool完成对leakTaskFactory和houseKeeperTask的设置操作。至此,一个HikariPool对象就创建完毕了。同时也完成了对HikariDataSource的pool和fastPathPool的赋值操作。

    4. this.seal();


    将继承HikariConfig类而复制过来的sealed值,从原本的false修改成true

    三. 从连接池里获取连接

    Connection connection = dataSource.getConnection();

    流程图如下:

     执行的就是HikariDataSource的getConnection方法。

    1. @Override
    2. public Connection getConnection() throws SQLException{
    3. if (isClosed()) {
    4. throw new SQLException("HikariDataSource " + this + " has been closed.");
    5. }
    6. if (fastPathPool != null) {
    7. return fastPathPool.getConnection();
    8. }
    9. // See http://en.wikipedia.org/wiki/Double-checked_locking#Usage_in_Java
    10. HikariPool result = pool;
    11. if (result == null) {
    12. synchronized (this) {
    13. result = pool;
    14. if (result == null) {
    15. validate();
    16. LOGGER.info("{} - Starting...", getPoolName());
    17. try {
    18. pool = result = new HikariPool(this);
    19. this.seal();
    20. }
    21. catch (PoolInitializationException pie) {
    22. if (pie.getCause() instanceof SQLException) {
    23. throw (SQLException) pie.getCause();
    24. }
    25. else {
    26. throw pie;
    27. }
    28. }
    29. LOGGER.info("{} - Start completed.", getPoolName());
    30. }
    31. }
    32. }
    33. return result.getConnection();
    34. }

    如果当前HikariDataSource的fastPathPool不为null,就通过fastPathPool来获取Connection。

    如果为null,再看pool对象,如果pool对象不为null,就通过pool来获取Connection。

    如果pool为null,那么使用synchronized对此类进行加锁,并且使用双重检查方式判断pool是否为null。如果还为null,就通过new HikariPool()创建一个pool,(也就是上述的流程执行一遍)。最终还是要使用pool创建一个Conneciton。

    程序逻辑转移到HikariPool的getConnection方法中

    1. public Connection getConnection() throws SQLException{
    2. return getConnection(connectionTimeout);
    3. }
    4. public Connection getConnection(final long hardTimeout) throws SQLException{
    5. suspendResumeLock.acquire();
    6. final long startTime = currentTime();
    7. try {
    8. long timeout = hardTimeout;
    9. do {
    10. PoolEntry poolEntry = connectionBag.borrow(timeout, MILLISECONDS);
    11. if (poolEntry == null) {
    12. break; // We timed out... break and throw exception
    13. }
    14. final long now = currentTime();
    15. if (poolEntry.isMarkedEvicted() || (elapsedMillis(poolEntry.lastAccessed, now) > aliveBypassWindowMs && !isConnectionAlive(poolEntry.connection))) {
    16. closeConnection(poolEntry, poolEntry.isMarkedEvicted() ? EVICTED_CONNECTION_MESSAGE : DEAD_CONNECTION_MESSAGE);
    17. timeout = hardTimeout - elapsedMillis(startTime);
    18. } else {
    19. metricsTracker.recordBorrowStats(poolEntry, startTime);
    20. return poolEntry.createProxyConnection(leakTaskFactory.schedule(poolEntry), now);
    21. }
    22. } while (timeout > 0L);
    23. metricsTracker.recordBorrowTimeoutStats(startTime);
    24. throw createTimeoutException(startTime);
    25. } catch (InterruptedException e) {
    26. Thread.currentThread().interrupt();
    27. throw new SQLException(poolName + " - Interrupted during connection acquisition", e);
    28. } finally {
    29. suspendResumeLock.release();
    30. }
    31. }

    会先通过suspendResumeLock进行加锁acquire,默认使用无锁方式,所以这个方法直接跳过了。

    在没超时的do while循环里,先通过connectionBag的borrow方法得到一个PoolEntry对象,borrow方法如下:

    1. public T borrow(long timeout, final TimeUnit timeUnit) throws InterruptedException{
    2. // Try the thread-local list first
    3. final List list = threadList.get();
    4. for (int i = list.size() - 1; i >= 0; i--) {
    5. final Object entry = list.remove(i);
    6. @SuppressWarnings("unchecked")
    7. final T bagEntry = weakThreadLocals ? ((WeakReference) entry).get() : (T) entry;
    8. if (bagEntry != null && bagEntry.compareAndSet(STATE_NOT_IN_USE, STATE_IN_USE)) {
    9. return bagEntry;
    10. }
    11. }
    12. // Otherwise, scan the shared list ... then poll the handoff queue
    13. final int waiting = waiters.incrementAndGet();
    14. try {
    15. for (T bagEntry : sharedList) {
    16. if (bagEntry.compareAndSet(STATE_NOT_IN_USE, STATE_IN_USE)) {
    17. // If we may have stolen another waiter's connection, request another bag add.
    18. if (waiting > 1) {
    19. listener.addBagItem(waiting - 1);
    20. }
    21. return bagEntry;
    22. }
    23. }
    24. listener.addBagItem(waiting);
    25. timeout = timeUnit.toNanos(timeout);
    26. do {
    27. final long start = currentTime();
    28. final T bagEntry = handoffQueue.poll(timeout, NANOSECONDS);
    29. if (bagEntry == null || bagEntry.compareAndSet(STATE_NOT_IN_USE, STATE_IN_USE)) {
    30. return bagEntry;
    31. }
    32. timeout -= elapsedNanos(start);
    33. } while (timeout > 10_000);
    34. return null;
    35. } finally {
    36. waiters.decrementAndGet();
    37. }
    38. }
    39. 在borrow方法里,首先从ThreadLocal的本地线程threadList里获取bagEntry,前提是这个list里有,并且这个bagEntry通过CAS设置state=1(USE)成功。不成功就只是单纯从列表里移除了这个bagEntry。

      从ThreadLocal里获取不到说明当前线程属于一个竞争获取连接的行为,那么就先设置waiters加1,然后从CopyOnWriteArrayList的sharedList获取,state=0的。如果state=0更改为1成功,那么这个bagEntry就被获取返回。在返回之前判断,如果当前waiters大于1,执行HikariPool的addBagItem方法。

      如果没从sharedList拿到bagEntry对象,那就先执行HikariPool的addBagItem方法。

      1. public void addBagItem(final int waiting){
      2. final boolean shouldAdd = waiting - addConnectionQueueReadOnlyView.size() >= 0; // Yes, >= is intentional.
      3. if (shouldAdd) {
      4. addConnectionExecutor.submit(poolEntryCreator);
      5. }else {
      6. logger.debug("{} - Add connection elided, waiting {}, queue {}", poolName, waiting, addConnectionQueueReadOnlyView.size());
      7. }
      8. }

      如果当前waiters还小于maxPoolSize,那就让线程池addConnectionExecutor执行早已初始化的内部类PoolEntryCreator线程。

      PoolEntryCreator线程的核心还是这个call方法

      1. @Override
      2. public Boolean call(){
      3. long sleepBackoff = 250L;
      4. while (poolState == POOL_NORMAL && shouldCreateAnotherConnection()) {
      5. final PoolEntry poolEntry = createPoolEntry();
      6. if (poolEntry != null) {
      7. connectionBag.add(poolEntry);
      8. logger.debug("{} - Added connection {}", poolName, poolEntry.connection);
      9. if (loggingPrefix != null) {
      10. logPoolState(loggingPrefix);
      11. }
      12. return Boolean.TRUE;
      13. }
      14. // failed to get connection from db, sleep and retry
      15. if (loggingPrefix != null) logger.debug("{} - Connection add failed, sleeping with backoff: {}ms", poolName, sleepBackoff);
      16. quietlySleep(sleepBackoff);
      17. sleepBackoff = Math.min(SECONDS.toMillis(10), Math.min(connectionTimeout, (long) (sleepBackoff * 1.5)));
      18. }
      19. // Pool is suspended or shutdown or at max size
      20. return Boolean.FALSE;
      21. }

      如果当前连接池状态正常(等于0,默认就是0),并且此时应该创建新的连接(已有连接数还小于最大连接数 同时 有等待的线程或者当前空闲连接还不够配置的值)。那么就执行createPoolEntry方法,去创建Connection对象并得到poolEntry对象。

      得到poolEntry对象后,就执行ConcurrentBag的add方法(将poolEntry对象放在sharedList中,同时,如果将waiters大于0 且此entry对象还没使用,就给ConcurrentBag的队列handoffQueue里放入)。

      得不到poolEntry对象时线程就先sleep而后再进行while循环。

      此时只是让别的线程给ConcurrentBag的队列里放入了bagEntry,原先获取连接的borrow方法还没结束。还需要从队列中拿出此bagEntry,然后作为方法结果返回。如果超时就返回null。最终的waiters个数一定要减一。

      此时上述返回的bagEntry对象就是带有Connection的最终PoolEntry对象。释放最初加的锁,然后方法返回Connection。此时返回的Connection是一个被代理后的ProxyConnection类。

      1. protected ProxyConnection(final PoolEntry poolEntry,
      2. final Connection connection,
      3. final FastList openStatements,
      4. final ProxyLeakTask leakTask,
      5. final long now,
      6. final boolean isReadOnly,
      7. final boolean isAutoCommit) {
      8. this.poolEntry = poolEntry;
      9. this.delegate = connection;
      10. this.openStatements = openStatements;
      11. this.leakTask = leakTask;
      12. this.lastAccess = now;
      13. this.isReadOnly = isReadOnly;
      14. this.isAutoCommit = isAutoCommit;
      15. }

      四. 关闭连接

      connection.close();

      流程图如下:

       对应的是ProxyConnection的close方法,因为得到Connection时,本质通过了代理方式得到的是这个ProxyConnection。

      1. @Override
      2. public final void close() throws SQLException{
      3. // Closing statements can cause connection eviction, so this must run before the conditional below
      4. closeStatements();
      5. if (delegate != ClosedConnection.CLOSED_CONNECTION) {
      6. leakTask.cancel();
      7. try {
      8. if (isCommitStateDirty && !isAutoCommit) {
      9. delegate.rollback();
      10. lastAccess = currentTime();
      11. LOGGER.debug("{} - Executed rollback on connection {} due to dirty commit state on close().", poolEntry.getPoolName(), delegate);
      12. }
      13. if (dirtyBits != 0) {
      14. poolEntry.resetConnectionState(this, dirtyBits);
      15. lastAccess = currentTime();
      16. }
      17. delegate.clearWarnings();
      18. } catch (SQLException e) {
      19. // when connections are aborted, exceptions are often thrown that should not reach the application
      20. if (!poolEntry.isMarkedEvicted()) {
      21. throw checkException(e);
      22. }
      23. }finally {
      24. delegate = ClosedConnection.CLOSED_CONNECTION;
      25. poolEntry.recycle(lastAccess);
      26. }
      27. }
      28. }

       这个close方法会先把属于这个connection对象里的statements对象数组清空。

      然后当前连接未标记关闭的话,就会进行事务回滚。

      如果当前有脏标记,会根据脏标记进行连接池对象的重新标记。

      最终会把当前这个给Connection设置为Closed状态,ProxyConnection所属的poolEntry执行recycle方法。

      1. void recycle(final long lastAccessed){
      2. if (connection != null) {
      3. this.lastAccessed = lastAccessed;
      4. hikariPool.recycle(this);
      5. }
      6. }


      跳到HikariPool里执行recycle

      1. void recycle(final PoolEntry poolEntry){
      2. metricsTracker.recordConnectionUsage(poolEntry);
      3. connectionBag.requite(poolEntry);
      4. }


      跳到ConcurrentBag里执行requite方法

      1. public void requite(final T bagEntry){
      2. bagEntry.setState(STATE_NOT_IN_USE);
      3. for (int i = 0; waiters.get() > 0; i++) {
      4. if (bagEntry.getState() != STATE_NOT_IN_USE || handoffQueue.offer(bagEntry)) {
      5. return;
      6. } else if ((i & 0xff) == 0xff) {
      7. parkNanos(MICROSECONDS.toNanos(10));
      8. } else {
      9. Thread.yield();
      10. }
      11. }
      12. final List threadLocalList = threadList.get();
      13. if (threadLocalList.size() < 50) {
      14. threadLocalList.add(weakThreadLocals ? new WeakReference<>(bagEntry) : bagEntry);
      15. }
      16. }
      17. 如果当前还有等待获取连接的线程,那么就把这个poolEntry放入到handoffQueue中,让等待的线程能够获取到。

        如果当前没有等待获取连接的线程,同时ThreadLocal的副本threadList个数还小于50,就把当前poolEntry对象放在threadList中。

        五. 关闭连接池

        1. 将连接池的isShutdown设置为true,其他线程如果看到这个状态,会暂停相关功能执行。
        2. pool对象还存在,就执行shutdown方法。
          1. 设置pool状态,设置为2,表示终止,其他线程如果看到这个状态,会暂停相关功能
          2. 将后台执行HouseKeeper线程的定时任务终止
          3. 循环CurrentBag里poolEntry所在的sharedList,遍历出来的poolEntry对象进行Connection的关闭
          4. 创建连接的线程池addConnectionExecutor关闭
          5. CurrentBag执行close方法
          6. 中断当前还在活跃的连接,并进行关闭
          7. 关闭其他线程池
        3. 完成终止

        总结

        HikariCP的连接池实现方式比较简单的。使用CocurrentBag类管理了线程副本、共享数组和队列第三个集合,从CocurrerntBag里获取连接的顺序是,线程副本 > 共享数组 > 队列。队列的作用是将创建连接和获取连接两个操作进行了分离。每创建一次连接,就会在共享数组里放一个。线程副本只有在执行连接的close方法时,才会放入当前连接到副本。

        在构建连接池的时候,会先创建一个连接来检测是否能连上数据库。这个连接就是初始连接,在检测完毕后并不会销毁。连接池创建完毕后,会启动定时的线程池执行线程检测任务,主要做两件事:1.如果当前总的连接数不满足配置,就创建连接;2.如果当前连接数超过配置要求,那么就销毁一部分连接,销毁顺序也是按照创建的时间,把最早的连接销毁。

      18. 相关阅读:
        代码随想录——接雨水(双指针&动态规划&单调栈)
        初心如磐、砥砺筑梦,谱写云原生时代新篇章
        币圈是什么意思?币圈开发
        Redis.conf配置文件说明
        R语言进度条:txtProgressBar功能使用方法
        Pytorch-MLP-Mnist
        HIVE操作自查手册(全)
        SyntaxError: EOL while scanning string literal
        Pytorch之EfficientNetV2图像分类
        十五、异常(1)
      19. 原文地址:https://blog.csdn.net/liulimoyu/article/details/128014414