水煮MyBatis(九)- 获取数据库链接的细节

89 阅读2分钟

前言

接上一章继续聊聊链接的问题,之前把mybatis自带的三种数据源做了个大概是说明,还留了个小任务:池化数据源如何获取链接。
很久以前我采用Common-pool工具包实现了一个FTP协议的连接池,现在回过头来看,其实连接池的实现方式都是相似的,本质上来说就是保活、复用的思想,拒绝一次性消耗品。

流程图

image.png

说一下主要步骤

  • 检查是否有空闲【idle】链接,如果有,则直接返回一个可用的链接;
  • 如果没有,则检查activeConnections集合是否达到上限,如果没有满,则创建一个链接,并放到activeConnections集合之后,并返回;
  • 如果上两步都不满足,则进行第三步:取出active集合中的首个链接【最老】,判定是否过期,如果过期,则从Active集合移除此链接,用此老链接创建新连接,作废老连接;
  • 执行最后一个逻辑分支:等待N秒,再次进入循环;

源码

源码比较长,这里给出关键代码简化版本

  private PooledConnection popConnection(String username, String password) throws SQLException {
    PooledConnection conn = null;
    while (conn == null) {
      synchronized (state) {
        if (!state.idleConnections.isEmpty()) {
          // 从闲置集合中返回
          conn = state.idleConnections.remove(0);
        } else {
          // active集合没有达到上限
          if (state.activeConnections.size() < poolMaximumActiveConnections) {
            // 直接创建返回
            conn = new PooledConnection(dataSource.getConnection(), this);
          } else {
            // 已经达到上限,取出最老链接
            PooledConnection oldestActiveConnection = state.activeConnections.get(0);
            long longestCheckoutTime = oldestActiveConnection.getCheckoutTime();
            // 检查最老链接自从上次检出后,是否达到过期时间,poolMaximumCheckoutTime默认20秒
            if (longestCheckoutTime > poolMaximumCheckoutTime) {
              // 用老链接参数创建新连接
              conn = new PooledConnection(oldestActiveConnection.getRealConnection(), this);
              // 作废老链接
              oldestActiveConnection.invalidate();
            } else {
              // 等待
              state.wait(poolTimeToWait);
            }
          }
        }
        // 如果获取成功,校验链接是否有效,如果有效,设置参数
        if (conn != null) {
          // 是否有效
          if (conn.isValid()) {
            // 设置检出时间
            conn.setCheckoutTimestamp(System.currentTimeMillis());
          } else {
            // 链接无效,重新进入循环
            conn = null;
          }
        }
      }
    }
    return conn;
  }

每次拿到链接,都会设置检出时间,如果链接在activeConnections集合中,等一个【poolMaximumCheckoutTime】周期之后,就会作废当前链接(前提是进入那个分支,才会触发)。

需要注意的是:这里的检出时间是可以配置的,默认20秒实在是太久了,万一并发上来了,系统性能瓶颈就会爆发。