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接口。当然这两个类在连接池初始化过程中也非常重要,详细讲起来可能又会占用大量篇幅,所以这篇文章先到这里。关于这两个类我们下一篇博客再做分析

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-10-10 17:36:47

C3P0连接池初始化过程分析之一的相关文章

C3P0连接池一些基本配置

1 C3P0连接池配置 2 数据库连接是一个耗费大量资源且相当慢的操作,所以为了提高性能和连接速度,诞生了连接池这样的概念. 3 在多用户并发操作过程中,连接池尤为重要. 4 它是将那些已连接的数据库连接存放在一个容器里(连接池),这样以后别人要连接数据库的时候,将不会重新建立数据库连接,会直接从连接池里取出可用的连接,用户使用完毕后,连接又重新还回到连接池中. 5 注意:连接池里的连接将会一直保存在内存里,即使你没用也是一样.所以这个时候你得权衡一下连接池的连接数量了. 6 7 <c3p0-c

Maven 工程下 Spring MVC 站点配置 (三) C3P0连接池与@Autowired的应用

Maven 工程下 Spring MVC 站点配置 (一) Maven 工程下 Spring MVC 站点配置 (二) Mybatis数据操作 前两篇文章主要是对站点和数据库操作配置进行了演示,如果单单实现这两个需求的话,那么基本足够,但是很多时候一个网站除了仅仅能够访问数据库是不够的,它还需要对性能以及更简化的步骤有着更多的要求,这一篇重点就是帮助我们如何去实现数据连接池管理与更简化便利的开发步骤. 如果你觉得自己能写出更高效率的连接池,那你可以不需要这篇文章了,我更建议你可以去开源组织毛遂自

java学习笔记—c3p0连接池与元数据分析(42)

第一步:导入c3p0包 第二步:在classpath目录下,创建一个c3p0-config.xml <?xml version="1.0" encoding="UTF-8"?> <c3p0-config> <!-- 默认配置,只可以出现一次 --> <default-config> <!-- 连接超时设置30秒 --> <property name="checkoutTimeout"

C3P0连接池配置

C3P0是一个开源的JDBC连接池,详情请google. 在spring中,C3P0的一些配置,介绍如下(只列了一部分,不是全部) C3P0更详细的配置项及其含义,请参考:http://www.mchange.com/projects/c3p0/index.html <!-- c3p0连接池配置 --> <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"&g

c3p0连接池 &amp; JdbcUtils

一.引入jar包 二.java代码 1 @Test 2 public void testXML() throws Exception { 3 // 创建c3p0连接池核心工具类 4 // 自动加载src下c3p0的配置文件[c3p0-config.xml] 5 ComboPooledDataSource dataSource = new ComboPooledDataSource();// 使用默认的配置 6 PreparedStatement pstmt = null; 7 8 // 获取连接

关于c3p0连接池连接mysql数据库需要注意的几点

什么是数据库连接池: 用池来管理Connection,这可以重复使用Connection.有了池,所以我们就不用自己来创建Connection,而是通过池来获取Connection对象. 当使用完Connection后,调用Connection的close()方法也不会真的关闭Connection,而是把Connection"归还"给池.池就可以再利用这个Connection对象了. 导入DBUtils的工具包:commons-dbutils-1.6.jar commons-dbuti

[转]C3P0连接池详细配置

<c3p0-config>   <default-config>  <!--当连接池中的连接耗尽的时候c3p0一次同时获取的连接数.Default: 3 -->  <property name="acquireIncrement">3</property>    <!--定义在从数据库获取新连接失败后重复尝试的次数.Default: 30 -->  <property name="acquireRe

C3P0连接池配置方式

c3p0的配置方式分为三种,分别是 1.setters一个个地设置各个配置项 2.类路径下提供一个c3p0.properties文件 3.类路径下提供一个c3p0-config.xml文件 1.setters一个个地设置各个配置项 这种方式最繁琐,形式一般是这样: Properties props = new Properties(); InputStream in = ConnectionManager.class.getResourceAsStream("/c3p0.properties&q

c3p0连接池的学习

c3p0的学习 学习 学习 ! 首先c3p0的基本了解,在之前的web项目学习中都是直接使用jdbc的 DriverManager进行oracle数据库的连接,每次执行操作都会建立一个新的连接,在操作完成后,通过判断释放连接,但是如果处于高并发的情况下,就可能会造成服务器崩溃的后果,因为大量的资源同一时间得不到释放:以上是我自己对普通连接的学习和看法,采用c3p0连接池后,连接池会控制连接池内的连接对象数,以下是c3p0的xml配置文件: 1 <?xml version="1.0"