`
45808916
  • 浏览: 5705 次
文章分类
社区版块
存档分类
最新评论

C3P0连接池初始化过程分析之一

 
阅读更多

先把上一节的时序图拿来,强化一下印象,然后顺着图往下捋:

Created with Raphaël 2.1.0C3P0连接池初始化过程UserUserComboPooledDataSourceComboPooledDataSourceC3P0PooledConnectionPoolManagerC3P0PooledConnectionPoolManagerC3P0PooledConnectionPoolC3P0PooledConnectionPoolBasicResourcePoolBasicResourcePoolgetConnection()getPool()checkoutPooledConnection()checkoutResource()

核心类对应角色及作用

整个C3P0源码分析的第一篇应该已经介绍过了这几个核心类各自的角色及作用了,还不清楚的童鞋可以移步C3P0整体类结构简单分析去学习一下。这里就再说说它们之间的关系。

C3P0PooledConnectionPoolManager创建过程

ComboPooledDataSourceC3P0PooledConnectionPoolManager是一对一的关系,并且只会在初始化过程创建一次。C3P0PooledConnectionPoolManager的构造方法签名如下:

    public C3P0PooledConnectionPoolManager(ConnectionPoolDataSource cpds,
            Map flatPropertyOverrides, Map forceUserOverrides,
            int num_task_threads, String parentDataSourceIdentityToken,
            String parentDataSourceName) throws SQLException;

但是在ComboPooledDataSource创建C3P0PooledConnectionPoolManager的过程中,flatPropertyOverridesforceUserOverrides都是null,所以为了看起来更清晰,下面的构造方法是简化过的版本:

    public C3P0PooledConnectionPoolManager(ConnectionPoolDataSource cpds,
            Map flatPropertyOverrides, Map forceUserOverrides,
            int num_task_threads, String parentDataSourceIdentityToken,
            String parentDataSourceName) throws SQLException {
        try {
            this.cpds = cpds;
            this.flatPropertyOverrides = flatPropertyOverrides;
            this.num_task_threads = num_task_threads;
            this.parentDataSourceIdentityToken = parentDataSourceIdentityToken;
            this.parentDataSourceName = parentDataSourceName;

            DbAuth auth = null;

            if (auth == null)
                auth = C3P0ImplUtils.findAuth(cpds);

            this.defaultAuth = auth;

            Map tmp = new HashMap();
            BeanInfo bi = Introspector.getBeanInfo(cpds.getClass());
            PropertyDescriptor[] pds = bi.getPropertyDescriptors();
            PropertyDescriptor pd = null;
            for (int i = 0, len = pds.length; i < len; ++i) {
                pd = pds[i];

                String name = pd.getName();
                Method m = pd.getReadMethod();

                if (m != null)
                    tmp.put(name, m);
            }
            this.propNamesToReadMethods = tmp;

            if (forceUserOverrides == null) {
                Method uom = (Method) propNamesToReadMethods
                        .get("userOverridesAsString");
                if (uom != null) {
                    String uoas = (String) uom.invoke(cpds, (Object[]) null);
                    Map uo = C3P0ImplUtils.parseUserOverridesAsString(uoas);
                    this.userOverrides = uo;
                } else
                    this.userOverrides = Collections.EMPTY_MAP;
            } else
                this.userOverrides = forceUserOverrides;

            poolsInit();
        } catch (Exception e) {
            if (Debug.DEBUG)
                logger.log(MLevel.FINE, null, e);
            // e.printStackTrace();
            throw SqlUtils.toSQLException(e);
        }
    }

不要被一大堆代码给吓到了,其实这个构造方法很简单,除poolsInit();这一句之外,其他的代码都在做一件事——那就是重写一些配置而已。因此我们跟踪进poolsInit();

    private void poolsInit() {
        // 核心就这么一句,其余有些是防止热部署时内存泄露的
        maybePrivilegedPoolsInit(privilege_spawned_threads);
    }

    // 该方法也经过简化
    private void maybePrivilegedPoolsInit(final boolean privilege_spawned_threads){
        _poolsInit(); 
    }

    private synchronized void _poolsInit() {
        String idStr = idString();

        this.timer = new Timer(idStr + "-AdminTaskTimer", true);

        int matt = this.getMaxAdministrativeTaskTime();

        this.taskRunner = createTaskRunner(num_task_threads, matt, timer, idStr
                + "-HelperThread");

        int num_deferred_close_threads = this
                .getStatementCacheNumDeferredCloseThreads();

        if (num_deferred_close_threads > 0)
            this.deferredStatementDestroyer = createTaskRunner(
                    num_deferred_close_threads, matt, timer, idStr
                            + "-DeferredStatementDestroyerThread");
        else
            this.deferredStatementDestroyer = null;

        // 写死的为fasle
        if (POOL_EVENT_SUPPORT)
            this.rpfact = ResourcePoolFactory.createInstance(taskRunner, null,
                    timer);
        else
            this.rpfact = BasicResourcePoolFactory
                    .createNoEventSupportInstance(taskRunner, timer);

        this.authsToPools = new HashMap();
    }

可以看到上面最核心的方法就是_poolsInit(),我们梳理一下它做了哪些事呢:

  1. 创建定时器AdminTaskTimer
  2. 创建线程池,其中包含num_task_threadsHelperThread
  3. 可选项,缓存的statement过期销毁任务,这个只要在配置了num_deferred_close_threads 选项后才会生效
  4. 创建BasicResourcePoolFactory对象
  5. 创建缓存C3P0CollectionPoolMap

C3P0PooledConnectionPool创建过程

到这里,C3P0PooledConnectionPoolManager的创建工作就完毕了,那么下一步就是通过C3P0PooledConnectionPoolManager来创建C3P0PooledConnectionPool了。

    private C3P0PooledConnectionPool createPooledConnectionPool(DbAuth auth)
            throws SQLException {
        String userName = auth.getUser();
        String automaticTestTable = getAutomaticTestTable(userName);
        String realTestQuery;

        if (automaticTestTable != null) {
            realTestQuery = initializeAutomaticTestTable(automaticTestTable,
                    auth);
            if (this.getPreferredTestQuery(userName) != null) {
                if (logger.isLoggable(MLevel.WARNING)) {
                    logger.logp(
                            MLevel.WARNING,
                            C3P0PooledConnectionPoolManager.class.getName(),
                            "createPooledConnectionPool",
                            "[c3p0] Both automaticTestTable and preferredTestQuery have been set! "
                                    + "Using automaticTestTable, and ignoring preferredTestQuery. Real test query is ''{0}''.",
                            realTestQuery);
                }
            }
        } else {
            if (!defaultAuth.equals(auth))
                ensureFirstConnectionAcquisition(auth);

            realTestQuery = this.getPreferredTestQuery(userName);
        }

        C3P0PooledConnectionPool out = new C3P0PooledConnectionPool(cpds, auth,
                this.getMinPoolSize(userName), this.getMaxPoolSize(userName),
                this.getInitialPoolSize(userName),
                this.getAcquireIncrement(userName),
                this.getAcquireRetryAttempts(userName),
                this.getAcquireRetryDelay(userName),
                this.getBreakAfterAcquireFailure(userName),
                this.getCheckoutTimeout(userName),
                this.getIdleConnectionTestPeriod(userName),
                this.getMaxIdleTime(userName),
                this.getMaxIdleTimeExcessConnections(userName),
                this.getMaxConnectionAge(userName),
                this.getPropertyCycle(userName),
                this.getUnreturnedConnectionTimeout(userName),
                this.getDebugUnreturnedConnectionStackTraces(userName),
                this.getForceSynchronousCheckins(userName),
                this.getTestConnectionOnCheckout(userName),
                this.getTestConnectionOnCheckin(userName),
                this.getMaxStatements(userName),
                this.getMaxStatementsPerConnection(userName),
                this.getConnectionTester(userName),
                this.getConnectionCustomizer(userName), realTestQuery, rpfact,
                taskRunner, deferredStatementDestroyer,
                parentDataSourceIdentityToken);
        return out;
    }

上面的过程也很简单,如果在配置连接池时配置了automaticTestTable 参数,那么会在创建C3P0PooledConnectionPool之前先检测是否存在该表且为空表,如果没有则创建一张测试表,并且这个表是一张只包含一列的空表。这个表的目的就是为了之后检测连接有效性用的,检测过程就是用SELECT * FROM XXX,XXX为你的automaticTestTable参数值。
然后就开始调用构造方法创建C3P0PooledConnectionPool了,并且传入了一个BasicResourcePoolFactory用于让C3P0PooledConnectionPool创建BasicResourcePool

// 这里剔除了PooledConnectionResourcePoolManager的部分
C3P0PooledConnectionPool( final ConnectionPoolDataSource cpds,
                    final DbAuth auth,
                    int min, 
                    int max, 
                    int start,
                    int inc,
                    int acq_retry_attempts,
                    int acq_retry_delay,
                    boolean break_after_acq_failure,
                    int checkoutTimeout, //milliseconds
                    int idleConnectionTestPeriod, //seconds
                    int maxIdleTime, //seconds
                    int maxIdleTimeExcessConnections, //seconds
                    int maxConnectionAge, //seconds
                    int propertyCycle, //seconds
                    int unreturnedConnectionTimeout, //seconds
                    boolean debugUnreturnedConnectionStackTraces,
                    boolean forceSynchronousCheckins,
                    final boolean testConnectionOnCheckout,
                    final boolean testConnectionOnCheckin,
                    int maxStatements,
                    int maxStatementsPerConnection,
            /* boolean statementCacheDeferredClose,      */
                    final ConnectionTester connectionTester,
                    final ConnectionCustomizer connectionCustomizer,
                    final String testQuery,
                    final ResourcePoolFactory fact,
                    ThreadPoolAsynchronousRunner taskRunner,
            ThreadPoolAsynchronousRunner deferredStatementDestroyer,
                    final String parentDataSourceIdentityToken) throws SQLException
                    {
        try
        {
            // 这个scache是用来缓存statement的,这里先不介绍
            if (maxStatements > 0 && maxStatementsPerConnection > 0)
                this.scache = new DoubleMaxStatementCache( taskRunner, deferredStatementDestroyer, maxStatements, maxStatementsPerConnection );
            else if (maxStatementsPerConnection > 0)
                this.scache = new PerConnectionMaxOnlyStatementCache( taskRunner, deferredStatementDestroyer, maxStatementsPerConnection );
            else if (maxStatements > 0)
                this.scache = new GlobalMaxOnlyStatementCache( taskRunner, deferredStatementDestroyer, maxStatements );
            else
                this.scache = null;

            // 用来检测连接有效性的Tester
            this.connectionTester = connectionTester;

            // 获取连接时的最长等待时间
            this.checkoutTimeout = checkoutTimeout;

            // 执行任务的线程池
            this.sharedTaskRunner = taskRunner;
            // 销毁过期的statement的线程
            this.deferredStatementDestroyer = deferredStatementDestroyer;

            // 这个写死为true
            this.c3p0PooledConnections = (cpds instanceof WrapperConnectionPoolDataSource);
            this.effectiveStatementCache = c3p0PooledConnections && (scache != null);

            this.inUseLockFetcher = (c3p0PooledConnections ? C3P0_POOLED_CONNECION_NESTED_LOCK_LOCK_FETCHER : RESOURCE_ITSELF_IN_USE_LOCK_FETCHER);

            class PooledConnectionResourcePoolManager implements ResourcePool.Manager
            {   
                // 省略....
            }

            ResourcePool.Manager manager = new PooledConnectionResourcePoolManager();

            synchronized (fact)
            {
                fact.setMin( min );
                fact.setMax( max );
                fact.setStart( start );
                fact.setIncrement( inc );
                fact.setIdleResourceTestPeriod( idleConnectionTestPeriod * 1000);
                fact.setResourceMaxIdleTime( maxIdleTime * 1000 );
                fact.setExcessResourceMaxIdleTime( maxIdleTimeExcessConnections * 1000 );
                fact.setResourceMaxAge( maxConnectionAge * 1000 );
                fact.setExpirationEnforcementDelay( propertyCycle * 1000 );
                fact.setDestroyOverdueResourceTime( unreturnedConnectionTimeout * 1000 );
                fact.setDebugStoreCheckoutStackTrace( debugUnreturnedConnectionStackTraces );
                fact.setForceSynchronousCheckins( forceSynchronousCheckins );
                fact.setAcquisitionRetryAttempts( acq_retry_attempts );
                fact.setAcquisitionRetryDelay( acq_retry_delay );
                fact.setBreakOnAcquisitionFailure( break_after_acq_failure );
                // 用一个ResourcePoolFactory创建出了一个ResourcePool
                rp = fact.createPool( manager );
            }
        }
        catch (ResourcePoolException e)
        { throw SqlUtils.toSQLException(e); }
    }

可以看到,在C3P0PooledConnectionPool被创建的过程中,还同时使用ResourcePoolFactory创建了一个ResourcePool,那么这个ResourcePool是用来干什么的呢?

首先我们知道,一个C3P0PooledConnectionPool对象就对应着一个数据库连接池。所以它具有各种连接池应该有的属性和方法,比如:

public PooledConnection checkoutPooledConnection() throws SQLException;

public void checkinPooledConnection(PooledConnection pcon) throws SQLException

我们就从这两个最基本的连接池方法的源码入手:

   public PooledConnection checkoutPooledConnection() throws SQLException {
        try {
            PooledConnection pc = (PooledConnection) this
                    .checkoutAndMarkConnectionInUse();
            pc.addConnectionEventListener(cl);
            return pc;
        } catch (TimeoutException e) {
            throw SqlUtils
                    .toSQLException(
                            "An attempt by a client to checkout a Connection has timed out.",
                            e);
        } catch (CannotAcquireResourceException e) {
            throw SqlUtils
                    .toSQLException(
                            "Connections could not be acquired from the underlying database!",
                            "08001", e);
        } catch (Exception e) {
            throw SqlUtils.toSQLException(e);
        }
    }

    private Object checkoutAndMarkConnectionInUse() throws TimeoutException,
            CannotAcquireResourceException, ResourcePoolException,
            InterruptedException {
        Object out = null;
        boolean success = false;
        while (!success) {
            try {
                // 这句就是取出连接的核心所在
                out = rp.checkoutResource(checkoutTimeout);
                if (out instanceof AbstractC3P0PooledConnection) {
                    // cast should succeed, because effectiveStatementCache
                    // implies c3p0 pooled Connections
                    AbstractC3P0PooledConnection acpc = (AbstractC3P0PooledConnection) out;
                    Connection physicalConnection = acpc
                            .getPhysicalConnection();
                    success = tryMarkPhysicalConnectionInUse(physicalConnection);
                } else
                    success = true; // we don't pool statements from non-c3p0
                                    // PooledConnections
            } finally {
                try {
                    if (!success && out != null)
                        rp.checkinResource(out);
                } catch (Exception e) {
                    logger
                            .log(
                                    MLevel.WARNING,
                                    "Failed to check in a Connection that was unusable due to pending Statement closes.",
                                    e);
                }
            }
        }
        return out;
    }


    public void checkinPooledConnection(PooledConnection pcon) throws SQLException
    { 
        //System.err.println(this + " -- CHECKIN");
        try 
        {
            pcon.removeConnectionEventListener( cl );
            unmarkConnectionInUseAndCheckin( pcon ); 
        } 
        catch (ResourcePoolException e)
        { throw SqlUtils.toSQLException(e); }
    }

    private void unmarkConnectionInUseAndCheckin(PooledConnection pcon) throws ResourcePoolException
    {
        if (effectiveStatementCache)
        {
            try
            {
                // cast should generally succeed, because effectiveStatementCache implies c3p0 pooled Connections
                // but clients can try to check-in whatever they want, so there are potential failures here
                AbstractC3P0PooledConnection acpc = (AbstractC3P0PooledConnection) pcon;
                Connection physicalConnection = acpc.getPhysicalConnection();
                unmarkPhysicalConnectionInUse(physicalConnection);
            }
            catch (ClassCastException e)
            {
                if (logger.isLoggable(MLevel.SEVERE))
                    logger.log(MLevel.SEVERE, 
                               "You are checking a non-c3p0 PooledConnection implementation into" +
                               "a c3p0 PooledConnectionPool instance that expects only c3p0-generated PooledConnections." +
                               "This isn't good, and may indicate a c3p0 bug, or an unusual (and unspported) use " +
                               "of the c3p0 library.", e);
            }
       }
       // 这句就是放回连接的核心所在
       rp.checkinResource(pcon);
    }

我们发现,其实所有关于连接池中的操作其实都是委托给了BasicResourcePool这个类来完成,并且这个类还有一个帮手,叫做BasicResourcePoolManager,实现了ResourcePool.Manager接口。当然这两个类在连接池初始化过程中也非常重要,详细讲起来可能又会占用大量篇幅,所以这篇文章先到这里。关于这两个类我们下一篇博客再做分析

<script type="text/javascript"> $(function () { $('pre.prettyprint code').each(function () { var lines = $(this).text().split('\n').length; var $numbering = $('<ul/>').addClass('pre-numbering').hide(); $(this).addClass('has-numbering').parent().append($numbering); for (i = 1; i <= lines; i++) { $numbering.append($('<li/>').text(i)); }; $numbering.fadeIn(1700); }); }); </script>
分享到:
评论

相关推荐

    c3p0连接池jar包

    C3P0连接池是Java开发中常用的数据库连接池组件,它能够有效地管理和优化数据库连接,提高应用程序的性能和效率。C3P0由Mithun Das Laskar创建,是一个开源项目,广泛应用于各种Java Web应用中。下面将详细介绍C3P0...

    C3P0连接池jar包(正式).zip

    C3P0是一个开源的Java数据库连接池,它实现了数据源和JNDI绑定,支持JDBC3规范和JDBC2的标准扩展。在Java应用程序中,数据库连接池扮演着非常重要的角色,它能够有效地管理数据库连接,提高系统性能,减少数据库资源...

    C3P0 连接池源码

    C3P0的配置可以通过XML或Java代码进行,包括初始化最小连接数、最大连接数、连接测试频率、超时时间等关键参数。例如,`minPoolSize`设定最小连接数,`maxPoolSize`设定最大连接数,`checkoutTimeout`设定获取连接...

    c3p0连接池工具

    c3p0是一个开源的JDBC连接池,它实现了数据源和JNDI绑定,支持JDBC3规范和JDBC2的标准扩展。c3p0的主要功能是管理数据库连接,提高数据库访问效率,避免因为频繁创建和销毁数据库连接而造成大量的系统资源浪费。在高...

    C3P0连接池管理类

    C3P0连接池管理类是数据库连接管理的一种机制,用于高效管理和复用数据库连接,以提高系统的性能和资源利用率。C3P0是一个开源的JDBC连接池,它实现了数据源和JNDI绑定,支持JDBC3规范和JDBC2的标准扩展。在Java应用...

    C3P0连接池配置

    C3P0是一个开源的JDBC连接池,它实现了数据源和JNDI绑定,支持JDBC3规范和JDBC2的标准扩展。C3P0的主要特点是轻量级、高效且稳定,广泛应用于各种Java Web应用中。在本文中,我们将深入探讨C3P0连接池的配置,以便更...

    c3p0连接池

    1. **连接池管理**:C3P0能够初始化一定数量的数据库连接,并在应用需要时提供这些连接,用完后回收到连接池中,避免了频繁创建和销毁连接带来的开销。 2. **自动检测与恢复**:C3P0可以定期检查数据库连接的有效性...

    c3p0连接池插件

    2. **配置c3p0**:创建一个配置文件(如c3p0.properties或在应用的配置文件中添加c3p0相关配置),设定连接池的基本属性,如数据库驱动、用户名、密码、初始化连接数等。 3. **创建数据源**:在Java代码中,通过`...

    c3p0连接池以及配置文件

    C3P0是由Maurice Priess创建的一个开源项目,它旨在提供一个健壮、完全免费的JDBC连接池实现,以解决数据库连接管理的问题,提升系统的性能和稳定性。 ### C3P0基本概念 1. **数据库连接池**:数据库连接池在初始...

    c3p0连接池源码

    C3P0是一个开源的Java数据库...总之,C3P0是一个强大且灵活的数据库连接池实现,通过深入研究它的源码,开发者可以掌握数据库连接池的实现细节,提升对数据库管理及JDBC操作的理解,从而在开发过程中做出更优的选择。

    C3P0连接池技术.zip

    C3P0数据库连接池技术是一种广泛应用于Java后端开发中的开源组件,它提供了一种高效、灵活的方式来管理数据库连接,以优化应用程序的性能。C3P0版本号0.9.5.2是该库的一个稳定版本,包含了对多种数据库(如MySQL, ...

    C3P0连接池jar包.rar

    C3P0连接池是一个开源的Java数据库连接池,它实现了数据源和JNDI绑定,支持JDBC3规范和JDBC2的标准扩展。在Java应用程序中,数据库连接的创建和销毁是耗时操作,而连接池则可以有效地管理这些连接,避免频繁地创建和...

    C3P0连接池jar包(完整版).rar

    C3P0是一个开源的JDBC连接池,它实现了数据源和JNDI绑定,支持JDBC3规范和JDBC2的标准扩展。这个压缩包包含了使用C3P0连接池所需的全部组件,包括mchange-commons-java库和C3P0的核心库。以下是关于C3P0连接池及其...

    c3p0连接池配置使用

    c3p0是一个开源的JDBC连接池,它实现了数据源和JNDI绑定,支持JDBC3规范和JDBC2的标准扩展。使用c3p0连接池可以有效地管理和优化数据库连接,提高系统的性能和稳定性。下面将详细介绍c3p0的配置和使用方法。 ### 1....

    c3p0数据库连接池案例

    3. 初始化c3p0连接池:在Java代码中,通过`ComboPooledDataSource`类实例化一个连接池,并加载配置。 4. 获取和释放连接:在需要操作数据库的地方,通过连接池的`getConnection()`方法获取连接,使用完毕后调用`...

    c3p0连接池使用所需jar包

    C3P0是一个开源的JDBC连接池,它实现了数据源和JNDI绑定,支持JDBC3规范和JDBC2的标准扩展。在Java应用程序中,尤其是在Web应用中,使用数据库连接池可以有效地管理和控制数据库连接,提高系统的性能和效率。下面...

    C3P0连接池.zip

    C3P0连接池是一个广泛使用的Java数据库连接池实现,它允许程序在多个数据库操作之间重用数据库连接,从而提高应用程序的性能和效率。数据库连接池管理着数据库连接的创建、分配、回收和销毁,避免了频繁的创建和关闭...

    c3p0数据库连接池

    **c3p0数据库连接池**是Java后端开发中常用的一个开源的数据库连接池组件,主要用于管理和优化数据库连接。它的全称是ComMchange V3 Pooled Database Connections,由Maurice Priess创建,旨在提供一个高效、灵活且...

Global site tag (gtag.js) - Google Analytics