mybatis系列-connection连接池解析

连接池主要是两个逻辑,首先是获取连接的逻辑,结合代码来讲一讲

private PooledConnection popConnection(String username, String password) throws SQLException {
    boolean countedWait = false;
    PooledConnection conn = null;
    long t = System.currentTimeMillis();
    int localBadConnectionCount = 0;

    // 循环获取连接
    while (conn == null) {
      // 加锁
      lock.lock();
      try {
        // 如果闲置的连接列表不为空
        if (!state.idleConnections.isEmpty()) {
          // Pool has available connection
          // 连接池有可用的连接
          conn = state.idleConnections.remove(0);
          if (log.isDebugEnabled()) {
            log.debug("Checked out connection " + conn.getRealHashCode() + " from pool.");
          }
        } else {
          // Pool does not have available connection
          // 进入这个分支表示没有空闲连接,但是活跃连接数还没达到最大活跃连接数上限,那么这时候就可以创建一个新连接
          if (state.activeConnections.size() < poolMaximumActiveConnections) {
            // Can create new connection
            // 这里创建连接我们之前讲过,
            conn = new PooledConnection(dataSource.getConnection(), this);
            if (log.isDebugEnabled()) {
              log.debug("Created connection " + conn.getRealHashCode() + ".");
            }
          } else {
            // Cannot create new connection
            // 进到这个分支了就表示没法创建新连接了,那么怎么办呢,这里引入了一个 poolMaximumCheckoutTime,这代表了我去控制连接一次被使用的最长时间,如果超过这个时间了,我就要去关闭失效它
            PooledConnection oldestActiveConnection = state.activeConnections.get(0);
            long longestCheckoutTime = oldestActiveConnection.getCheckoutTime();
            if (longestCheckoutTime > poolMaximumCheckoutTime) {
              // Can claim overdue connection
              // 所有超时连接从池中被借出的次数+1
              state.claimedOverdueConnectionCount++;
              // 所有超时连接从池中被借出并归还的时间总和 + 当前连接借出时间
              state.accumulatedCheckoutTimeOfOverdueConnections += longestCheckoutTime;
              // 所有连接从池中被借出并归还的时间总和 + 当前连接借出时间
              state.accumulatedCheckoutTime += longestCheckoutTime;
              // 从活跃连接数中移除此连接
              state.activeConnections.remove(oldestActiveConnection);
              // 如果该连接不是自动提交的,则尝试回滚
              if (!oldestActiveConnection.getRealConnection().getAutoCommit()) {
                try {
                  oldestActiveConnection.getRealConnection().rollback();
                } catch (SQLException e) {
                  /*
                     Just log a message for debug and continue to execute the following
                     statement like nothing happened.
                     Wrap the bad connection with a new PooledConnection, this will help
                     to not interrupt current executing thread and give current thread a
                     chance to join the next competition for another valid/good database
                     connection. At the end of this loop, bad {@link @conn} will be set as null.
                   */
                  log.debug("Bad connection. Could not roll back");
                }
              }
              // 用此连接的真实连接再创建一个连接,并设置时间
              conn = new PooledConnection(oldestActiveConnection.getRealConnection(), this);
              conn.setCreatedTimestamp(oldestActiveConnection.getCreatedTimestamp());
              conn.setLastUsedTimestamp(oldestActiveConnection.getLastUsedTimestamp());
              oldestActiveConnection.invalidate();
              if (log.isDebugEnabled()) {
                log.debug("Claimed overdue connection " + conn.getRealHashCode() + ".");
              }
            } else {
              // Must wait
              // 这样还是获取不到连接就只能等待了
              try {
                // 标记状态,然后把等待计数+1
                if (!countedWait) {
                  state.hadToWaitCount++;
                  countedWait = true;
                }
                if (log.isDebugEnabled()) {
                  log.debug("Waiting as long as " + poolTimeToWait + " milliseconds for connection.");
                }
                long wt = System.currentTimeMillis();
                // 等待 poolTimeToWait 时间
                condition.await(poolTimeToWait, TimeUnit.MILLISECONDS);
                // 记录等待时间
                state.accumulatedWaitTime += System.currentTimeMillis() - wt;
              } catch (InterruptedException e) {
                // set interrupt flag
                Thread.currentThread().interrupt();
                break;
              }
            }
          }
        }
        // 如果连接不为空
        if (conn != null) {
          // ping to server and check the connection is valid or not
          // 判断是否有效
          if (conn.isValid()) {
            if (!conn.getRealConnection().getAutoCommit()) {
              // 回滚未提交的
              conn.getRealConnection().rollback();
            }
            conn.setConnectionTypeCode(assembleConnectionTypeCode(dataSource.getUrl(), username, password));
            // 设置时间
            conn.setCheckoutTimestamp(System.currentTimeMillis());
            conn.setLastUsedTimestamp(System.currentTimeMillis());
            // 添加进活跃连接
            state.activeConnections.add(conn);
            state.requestCount++;
            state.accumulatedRequestTime += System.currentTimeMillis() - t;
          } else {
            if (log.isDebugEnabled()) {
              log.debug("A bad connection (" + conn.getRealHashCode() + ") was returned from the pool, getting another connection.");
            }
            // 连接无效,坏连接+1
            state.badConnectionCount++;
            localBadConnectionCount++;
            conn = null;
            // 如果坏连接已经超过了容忍上限,就抛异常
            if (localBadConnectionCount > (poolMaximumIdleConnections + poolMaximumLocalBadConnectionTolerance)) {
              if (log.isDebugEnabled()) {
                log.debug("PooledDataSource: Could not get a good connection to the database.");
              }
              throw new SQLException("PooledDataSource: Could not get a good connection to the database.");
            }
          }
        }
      } finally {
        // 释放锁
        lock.unlock();
      }

    }

    if (conn == null) {
      // 连接仍为空
      if (log.isDebugEnabled()) {
        log.debug("PooledDataSource: Unknown severe error condition.  The connection pool returned a null connection.");
      }
      // 抛出异常
      throw new SQLException("PooledDataSource: Unknown severe error condition.  The connection pool returned a null connection.");
    }
    // fanhui 
    return conn;
  }

然后是还回连接

protected void pushConnection(PooledConnection conn) throws SQLException {
    // 加锁
    lock.lock();
    try {
      // 从活跃连接中移除当前连接
      state.activeConnections.remove(conn);
      if (conn.isValid()) {
        // 当前的空闲连接数小于连接池中允许的最大空闲连接数
        if (state.idleConnections.size() < poolMaximumIdleConnections && conn.getConnectionTypeCode() == expectedConnectionTypeCode) {
          // 记录借出时间
          state.accumulatedCheckoutTime += conn.getCheckoutTime();
          if (!conn.getRealConnection().getAutoCommit()) {
            // 同样是做回滚
            conn.getRealConnection().rollback();
          }
          // 新建一个连接
          PooledConnection newConn = new PooledConnection(conn.getRealConnection(), this);
          // 加入到空闲连接列表中
          state.idleConnections.add(newConn);
          newConn.setCreatedTimestamp(conn.getCreatedTimestamp());
          newConn.setLastUsedTimestamp(conn.getLastUsedTimestamp());
          // 原连接失效
          conn.invalidate();
          if (log.isDebugEnabled()) {
            log.debug("Returned connection " + newConn.getRealHashCode() + " to pool.");
          }
          // 提醒前面等待的
          condition.signal();
        } else {
          // 上面是相同的,就是这里是空闲连接数已经超过上限
          state.accumulatedCheckoutTime += conn.getCheckoutTime();
          if (!conn.getRealConnection().getAutoCommit()) {
            conn.getRealConnection().rollback();
          }
          conn.getRealConnection().close();
          if (log.isDebugEnabled()) {
            log.debug("Closed connection " + conn.getRealHashCode() + ".");
          }
          conn.invalidate();
        }
      } else {
        if (log.isDebugEnabled()) {
          log.debug("A bad connection (" + conn.getRealHashCode() + ") attempted to return to the pool, discarding connection.");
        }
        state.badConnectionCount++;
      }
    } finally {
      lock.unlock();
    }
  }