c3p0相关

由于一些历史原因,工作中部分项目还在使用c3p0。这个连接池相对古老,问题相对较多,所以抽时间稍微了解了下。

官方或者maven仓库提供的0.9.1.2版本的source,或多或少的有些编译问题。或者是部分类是编译时生成的,比如Newxxx等类。下面的分支对其做了补齐。

没有编译错误的代码分支

demo在这里

整体大致逻辑有:获取(取出)连接、归还连接、创建真实物理连接、最小最大连接数、idle时间、idle检测等等。

  • 从C3P0PooledConnectionPoolManager中拿到C3P0PooledConnectionPool com.mchange.v2.c3p0.impl.C3P0PooledConnectionPoolManager.getPool(DbAuth)

  • 获取(取出)连接checkout
    从C3P0PooledConnectionPool中checkout出PooledConnection
    PooledConnectioBasicResourcePooln(代码中具体的是NewPooledConnection) 是在BasicResourcePool 中管理的, 代码中简称rp
    也是有关闭的概念 一旦关闭了 就不能再从里面checkout出连接了

  • new proxy
    NewProxyConnection (physicalConnection—com.mysql.jdbc.JDBC4Connection) NewPooledConnection.java
    具体关系是这样的:
    是在NewPooledConnection中构建 了NewProxyConnection,构建NewProxyConnection时送进去的构造参数的也就是真正连接是physicalConnection(该实例对应的类是com.mysql.jdbc.JDBC4Connection), NewProxyConnection的parentPooledConnection又被设置成构造他的NewPooledConnection实例。
    然后真正返回给业务侧使用的也就是NewProxyConnection实例。

        public synchronized Connection getConnection() throws SQLException
        {
            try
            {
                //throw new SQLException("NOT IMPLEMENTED");
                if ( exposedProxy == null )
                {
                    exposedProxy = new NewProxyConnection( physicalConnection, this );
                }
                else
                {
    //              System.err.println("c3p0 -- Uh oh... getConnection() was called on a PooledConnection when " +
    //              "it had already provided a client with a Connection that has not yet been " +
    //              "closed. This probably indicates a bug in the connection pool!!!");
    
                    if ( logger.isLoggable( MLevel.WARNING ) )
                        logger.warning("c3p0 -- Uh oh... getConnection() was called on a PooledConnection when " +
                                        "it had already provided a client with a Connection that has not yet been " +
                        "closed. This probably indicates a bug in the connection pool!!!");
    
                }
                return exposedProxy;
            }
            catch ( Exception e )
            {
                SQLException sqle = handleThrowable( e );
                throw sqle;
            }
        }
  • 创建物理连接的地方
    com.mchange.v2.c3p0.DriverManagerDataSource.getConnection(String, String)

    com.mchange.v2.c3p0.DriverManagerDataSource
    创建物理连接的调用栈:

    @com.mchange.v2.c3p0.DriverManagerDataSource.getConnection()
        at com.mchange.v2.c3p0.WrapperConnectionPoolDataSource.getPooledConnection(WrapperConnectionPoolDataSource.java:183)
        at com.mchange.v2.c3p0.WrapperConnectionPoolDataSource.getPooledConnection(WrapperConnectionPoolDataSource.java:172)
        at com.mchange.v2.c3p0.impl.C3P0PooledConnectionPool$1PooledConnectionResourcePoolManager.acquireResource(C3P0PooledConnectionPool.java:139)
        at com.mchange.v2.resourcepool.BasicResourcePool.doAcquire(BasicResourcePool.java:1014)
        at com.mchange.v2.resourcepool.BasicResourcePool.access$3(BasicResourcePool.java:1010)
        at com.mchange.v2.resourcepool.BasicResourcePool$AcquireTask.run(BasicResourcePool.java:1810)
        at com.mchange.v2.c3p0.WrapperConnectionPoolDataSource.getPooledConnection(WrapperConnectionPoolDataSource.java:183)        at com.mchange.v2.async.ThreadPoolAsynchronousRunner$PoolThread.run(ThreadPoolAsynchronousRunner.java:547)
        at com.mchange.v2.c3p0.WrapperConnectionPoolDataSource.getPooledConnection(WrapperConnectionPoolDataSource.java:172)
        at com.mchange.v2.c3p0.impl.C3P0PooledConnectionPool$1PooledConnectionResourcePoolManager.acquireResource(C3P0PooledConnectionPool.java:139)
        at com.mchange.v2.resourcepool.BasicResourcePool.doAcquire(BasicResourcePool.java:1014)
        at com.mchange.v2.resourcepool.BasicResourcePool.access$3(BasicResourcePool.java:1010)
        at com.mchange.v2.resourcepool.BasicResourcePool$AcquireTask.run(BasicResourcePool.java:1810)
        at com.mchange.v2.async.ThreadPoolAsynchronousRunner$PoolThread.run(ThreadPoolAsynchronousRunner.java:547)

    创建真实连接AcquireTask任务创建的调用栈:

    ts=2021-08-01 11:19:33;thread_name=Timer-0;id=d;is_daemon=true;priority=5;TCCL=sun.misc.Launcher$AppClassLoader@18b4aac2
        @com.mchange.v2.resourcepool.BasicResourcePool$AcquireTask.<init>()
            at com.mchange.v2.resourcepool.BasicResourcePool.expandPool(BasicResourcePool.java:454)
            at com.mchange.v2.resourcepool.BasicResourcePool._recheckResizePool(BasicResourcePool.java:395)
            at com.mchange.v2.resourcepool.BasicResourcePool.recheckResizePool(BasicResourcePool.java:437)
            at com.mchange.v2.resourcepool.BasicResourcePool.ensureMinResources(BasicResourcePool.java:1571)
            at com.mchange.v2.resourcepool.BasicResourcePool.cullExpired(BasicResourcePool.java:1469)
            at com.mchange.v2.resourcepool.BasicResourcePool.access$12(BasicResourcePool.java:1442)
            at com.mchange.v2.resourcepool.BasicResourcePool$CullTask.run(BasicResourcePool.java:1937)
            at java.util.TimerThread.mainLoop(Timer.java:555)
            at java.util.TimerThread.run(Timer.java:505)
  • 归还连接的地方。close方法触发
    com.mchange.v2.resourcepool.BasicResourcePool.checkinResource(Object) 负责归还连接到资源池

    Thread [main] (Suspended (breakpoint at line 1215 in BasicResourcePool))  
      owns: BasicResourcePool  (id=34)  
      owns: NewProxyConnection  (id=35) 
      BasicResourcePool.doCheckinManaged(Object) line: 1215 
      BasicResourcePool.checkinResource(Object) line: 647 
      C3P0PooledConnectionPool$ConnectionEventListenerImpl.doCheckinResource(ConnectionEvent) line: 636 
      C3P0PooledConnectionPool$ConnectionEventListenerImpl.connectionClosed(ConnectionEvent) line: 630  
      ConnectionEventSupport.fireConnectionClosed() line: 55  
      NewPooledConnection.fireConnectionClosed() line: 510  
      NewPooledConnection.markClosedProxyConnection(NewProxyConnection, boolean) line: 381  
      NewProxyConnection.close() line: 78 
      C3poDemo1.testQuery() line: 77  
      C3poDemo1.main(String[]) line: 51
  • idle连接过期时间到了,会释放连接并重新创建真实连接达成expand pool。

通过不断的创建任务放到自己实现的线程池ThreadPoolAsynchronousRunner中去执行,达到;连接创建与归还的目的。

ThreadPoolAsynchronousRunnerPoolThread是执行线程
他的run方法不断将pendingTasks中任务拿出来进行执行
他的实例可以有多个 通过synchronized ( ThreadPoolAsynchronousRunner.this )搞定并发问题

c3p0的问题有:

  1. 归还的任务没有优先级,本意上我们希望它能被优先执行。
  2. 多处使用synchronized
  3. 没有监控与度量。对于连接池,我们需要监控:获取连接对象的时间,在使用的连接数量,等待线程数量、idle的连接数量,连接出池时间,创建真实连接的数量与频率,创建真实连接错误的次数和原因等。
  4. 连接失败时,会进行重试,默认30次重试,每次等待1s。但是这个重试一直到30次全用完才会报错,如果你重试了10次成功了,他并不报错。在在一些诸如dns解析数据库域名异常时的场景,我们只看到接口耗时过大,并不能真正了解原因。具体可以参见AcquireTask的run方法中catch异常的部分的代码。

本博客所有文章除特别声明外,均采用 CC BY-SA 3.0协议 。转载请注明出处!

[leveldb]leveldb源码阅读 上一篇
kindergarten-exercises 下一篇