c3p0相关
由于一些历史原因,工作中部分项目还在使用c3p0。这个连接池相对古老,问题相对较多,所以抽时间稍微了解了下。
官方或者maven仓库提供的0.9.1.2
版本的source,或多或少的有些编译问题。或者是部分类是编译时生成的,比如Newxxx等类。下面的分支对其做了补齐。
整体大致逻辑有:获取(取出)连接、归还连接、创建真实物理连接、最小最大连接数、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中去执行,达到;连接创建与归还的目的。
ThreadPoolAsynchronousRunner
中PoolThread
是执行线程
他的run方法不断将pendingTasks中任务拿出来进行执行
他的实例可以有多个 通过synchronized ( ThreadPoolAsynchronousRunner.this )搞定并发问题
c3p0的问题有:
- 归还的任务没有优先级,本意上我们希望它能被优先执行。
- 多处使用synchronized
- 没有监控与度量。对于连接池,我们需要监控:获取连接对象的时间,在使用的连接数量,等待线程数量、idle的连接数量,连接出池时间,创建真实连接的数量与频率,创建真实连接错误的次数和原因等。
- 连接失败时,会进行重试,默认30次重试,每次等待1s。但是这个重试一直到30次全用完才会报错,如果你重试了10次成功了,他并不报错。在在一些诸如dns解析数据库域名异常时的场景,我们只看到接口耗时过大,并不能真正了解原因。具体可以参见AcquireTask的run方法中catch异常的部分的代码。
本博客所有文章除特别声明外,均采用 CC BY-SA 3.0协议 。转载请注明出处!