乐趣区

关于数据库:稳从数据库连接池-testOnBorrow-看架构设计-京东云技术团队

本文从 Commons DBCP testOnBorrow 的作用机制着手,管中窥豹,从一点去剖析数据库连接池获取的过程以及架构分层设计。

以下内容会依照每层的作用,贯通剖析整个调用流程。

1️⃣框架层 commons-pool

The indication of whether objects will be validated before being borrowed from the pool.

If the object fails to validate, it will be dropped from the pool, and we will attempt to borrow another.

testOnBorrow 不是 dbcp 定义的,是 commons-pool 定义的。commons-pool 具体的定义了资源池应用的一套标准和运行流程。

/**
 * Borrow an object from the pool. get object from 资源池
 * @see org.apache.commons.pool2.impl.GenericObjectPool#borrowObject(long)
 */
public T borrowObject(final long borrowMaxWaitMillis) throws Exception {
    
    PooledObject<T> p = null;
    
    // if validation fails, the instance is destroyed and the next available instance is examined. 
    // This continues until either a valid instance is returned or there are no more idle instances available.
    while (p == null) {
        // If there is one or more idle instance available in the pool, 
        // then an idle instance will be selected based on the value of getLifo(), activated and returned.
        p = idleObjects.pollFirst();
        if (p != null) {
            // 设置 testOnBorrow 就会进行可用性校验
            if (p != null && (getTestOnBorrow() || create && getTestOnCreate())) {
                boolean validate = false;
                Throwable validationThrowable = null;
                try {
                    // 具体的校验实现由实现类实现。// see org.apache.commons.dbcp2.PoolableConnectionFactory
                    validate = factory.validateObject(p);
                } catch (final Throwable t) {PoolUtils.checkRethrow(t);
                    validationThrowable = t;
                }
                if (!validate) {
                    try {
                        // 如果校验异样,会销毁该资源。// obj is not valid and should be dropped from the pool
                        destroy(p);
                        destroyedByBorrowValidationCount.incrementAndGet();} catch (final Exception e) {// Ignore - validation failure is more important}
                    p = null;
                }
            }
        }
    }

    return p.getObject();}

2️⃣应用层 commons-dbcp

dbcp 是特定于治理数据库连贯的资源池。

PoolableConnectionFactory is a PooledObjectFactory

PoolableConnection is a PooledObject

/**
 * @see PoolableConnectionFactory#validateObject(PooledObject)
 */
@Override
public boolean validateObject(final PooledObject<PoolableConnection> p) {
    try {
        /**
         * 检测资源池对象的创立工夫,是否超过生存工夫
         * 如果超过 maxConnLifetimeMillis, 不再委托数据库连贯进行校验,间接废除改资源
         * @see PoolableConnectionFactory#setMaxConnLifetimeMillis(long)
         */
        validateLifetime(p);
        // 委托数据库连贯进行自我校验
        validateConnection(p.getObject());
        return true;
    } catch (final Exception e) {return false;}
}

/**
 * 数据库连贯层的校验。具体到是否已敞开、是否与 server 连贯可用
 * @see Connection#isValid(int)
 */
public void validateConnection(final PoolableConnection conn) throws SQLException {if(conn.isClosed()) {throw new SQLException("validateConnection: connection closed");
    }
    conn.validate(_validationQuery, _validationQueryTimeout);
}

3️⃣根底层 mysql-connector-java

Returns true if the connection has not been closed and is still valid.

这个是 java.sql.Connection 定义的标准。具体实现依据对应数据库的 driver 来实现。应用某种机制用来探测连贯是否可用。

/**
 * 调用 com.mysql.jdbc.MysqlIO,发送 ping 申请,检测是否可用
 * 比照 H2 数据库,是通过获取以后事务级别来检测连贯是否能够。然而疏忽了 timeout 配置,毕竟是 demo 数据库 😅
 */
public synchronized boolean isValid(int timeout) throws SQLException {if (this.isClosed()) {return false;} else {
        try {this.pingInternal(false, timeout * 1000);
            return true;
        } catch (Throwable var5) {return false;}
    }
}

参考:MySQL 的连贯时长管制 –interactive_timeout 和 wait_timeout_翔云 123456 的博客 -CSDN 博客

总结

  • commons-pool 定义资源的 残缺申明周期接口,包含:makeObject、activateObject、validateObject、passivateObject、destoryObject。资源池治理对象,通过实现这些接口即可实现资源管制。参考:org.apache.commons.pool2.PooledObjectFactory
  • 在校验过程中,牵涉到很多工夫,包含资源池对象的创立工夫、生存工夫、数据库连贯的超时工夫、Mysql 连贯闲暇超时工夫等。不同层为了服务可靠性,提供不同的工夫配置。校验也是层层递进,最终委托到最底层来判断。
  • 校验过程中,对于连贯也会由是否已敞开的校验(isClosed())。包含 PoolableConnection#isClosed, Connection#isClosed, Socket#isClosed。同样也是层层保障,确保整个架构的牢靠。💪
  • 定义一套残缺谨严的标准和规范,比实现一个具体的性能或者个性要求更高 🎯。commons-pool 和 jdbc 定义了标准,commons-dbcp 和 mysql-connector-java 实现了具体的实现。有了标准和接口,组件和框架的对接和兼容才变为可能。

more 了解高可用

在浏览 MySQL Driver 源码过程中,有个点要特地记录下。以 MySQL Driver 创立连贯为例,用重试连贯实现可用性,这就是高可用。🎯

高可用不是一个口号,也不是简单的概念和公式。可能实实在在体系化的解决一类问题就是架构的目标。联合上述的架构分层,如果解决问题的计划通用性好,并且实现很优雅,就是好的架构。

// autoReconnect 
public void createNewIO(boolean isForReconnect) throws SQLException {synchronized (getConnectionMutex()) {
        // jdbc.url autoReconnect 指定为 true,辨认为 HighAvailability。emmm..... 🙉
        if (!getHighAvailability()) {connectOneTryOnly(isForReconnect, mergedProps);
            return;
        }
        // maxReconnects 默认为 3,重试失败的提醒就是:Attempted reconnect 3 times. Giving up.
        connectWithRetries(isForReconnect, mergedProps);
    }
}

作者:京东物流 杨攀

起源:京东云开发者社区

退出移动版