C3P0连接池解析
1,连接池的原理和意义
不管什么样的连接池其核心意义都是一致的,就是为一个项目创建连接对象,保存在池中待用。每次访问时从池中
获取已经存在的连接,使用完毕后,再返回池中。从而减少了创建连接和销毁连接而浪费的系统资源。本文会分析
目前常用连接池之一C3P0。
2,C3P0简单配置
c3p0连接池作为我们开发中最为常用的连接池之一使用起来是非常简单的。本文将从原理角度来分析一下 C3P0连
接池的主要架构。
一般只需要配置以下参数即可:
通常情况下,我们还会跟本地配置文件properties或者本地xml文件相结合的方式进行操作,以增强代码的灵活
性。
3,核心类
C3P0连接池主要核心类有以下几个:
ComboPooledDataSource:主要负责连接池的配置和获取池中的连接
C3P0PooledConnectionPoolManager:大内总管的角色。
负责生命周期以及生命周期所需要的一些参数。
比如:AdminTaskTimer定时检测和全局的线程池C3P0PoolConnectionPool:
表示物理连接池,本身的业务逻辑相对于比较简单,主要核心是通过代理ResourcePool来实现逻辑的。
BasicResourcePool:
是BasicResourcePool这个接口的实现类,是大内总管 C3P0PooledConnectionPoolManager的手下。
大内总管管池子,而BasicResourcePool是管池中连接的生命周期。
相当于大内中长春宫,宁寿宫等每一个宫的管事。
String driverClass = "com.mysql.jdbc.Driver";
String jdbcURL = "jdbc:mysql://localhost:3306/test";
String user = "root";
String password = "";
ComboPooledDataSource cpds = new ComboPooledDataSource();
cpds.setDriverClass(driverClass);
cpds.setJdbcUrl(jdbcURL);
cpds.setUser(user);
cpds.setPassword(password);
需要用的时候再获得其中的连接即可。
Connection conn = cpds.getConnection();
BasicResourcePool.Manager:
是BasicResourcePool的一个助手,协助一起管理连接池中的连接。
在BasicResourcePool中有几个任务需要注意:
1,创建连接ScatteredAcquireTask
2,销毁连接RemoveTask
3,检测连接是否过期CullTask
4,检测连接是否空闲CheckIdleResourcesTask
4,初始化特点
C3P0连接池在初始化的时候并不是在创建的时候直接创建并进行初始化的,连接池采取了一种“懒初始化”的思
想。那么何为懒初始化呢?
自己本身不会不会主动初始化,而是会等到第一个连接请求进来之后它才会初始化。而那个触发点就是:
当代码执行到这里,即获取到一个连接时,才会进行初始化保证了资源的有效利用。
5,创建连接从而降低系统资源消耗
众所周知,使用连接池中的连接是为了降低系统资源消耗,那么究竟是为什么呢?
首先数据库其实是一个管理系统,以MySQL为例,MySQL是一个数据库管理系统,区分服务端和客户端,MySQL
指的是这个整体管理系统,所以想要实际的把数据存入到表中,需要经历以下过程。
建立TCP连接,然后通过三次握手协议发送与相应;其次客户端需要进行账户验证,服务器返回确认;用户验证
后,需要传输相关连接变量如是否自动提交事务的设置等。
所以总而言之,操作数据之前,一定会有多次数据的交互,然后才能执行真正的执行CURD的sql语句。
连接池真正的降低的是以上的这些步骤,当需要对数据库进行操作时,直接获取已经存在的,就减少了资源的消
耗。
预先建立多个数据库连接对象,存入到池中。当有客户端进行请求的时候,取出一个连接为当前客户端的请求而操
作数据库,当该客户端请求操作完毕时,将请求放回连接池里。一直没有关闭。
6,连接池降低消耗的原理图解
ComboPooledDataSource cpds = new ComboPooledDataSource();
cpds.getConnection();
北京市昌平区建材城西路金燕龙办公楼一层 电话:400-618-9090
如果没有连接池,那么访问者跟数据库之间每次都创建一个连接,用完再销毁。
如果用了连接池,就不会自己创建,而是从池子中拿,用完再进行归还。
7,入口类ComboPooledDataSource
在进行初始化创建的时候,会有池子的入口类,就是ComboPooledDataSource。其中gettConnection方法是在
AbstractPoolBackedDataSource类中实现。
其中PooledConnection是javax.sql下的一个接口。针对于这个连接的执行者对象,结果集对象和连接对象对被
pooledconnection管理。而c3p0实现是NewPooledConnection。
8,BasicResourcePool类
8.1,主要核心代码:
其中timeout表示连接溢出时间。
查看池中是否有未使用的connection,如果有就返回,除此之外,还需要判断连接是否空闲通过timeout判断连接
是否过期,如果没有最大数,就生成一个,或者就等待。
8.2,主要核心代码:
AbstractPoolBackedDataSource类的getConnection方法如下:
public Connection getConnection() throwsSQLException{
PooledConnection pc = getPoolManager().getPool().checkoutPooledConnection();
returnpc.getConnection();
} O
bject resc = prelimCheckoutResource(timeout);
booleanrefurb = attemptRefurbishResourceOnCheckout(resc);
得到连接后,检测连接的可用性。 如果连接可用,接着判断连接是否处于管理中,如果不在就再调用本方法获取一
个,如果在,就返回本连接。
8.3,添加连接
获取连接时由BasicResourcePool类中的prelimCheckoutResource方法触发,调用过程如下:
1,BasicResourcePool类prelimCheckoutResource方法当可用连接数为0时调用recheckResizePool方法。
2,echeckResizePool方法判断需要扩容则调用expandPool方法。expandPool方法使用线程池调用AcquireTask
进行扩容。
3,AcquireTask调用mgr.acquireResource方法获取连接,如果连接池里的连接未到最大数量将获取的连接加入管
理和未使用的list中,否则销毁连接。
8.4,回收连接
由close方法触发。调用过程如下:
1,C3P0PooledConnectionPool类,的ConnectionEventListenerImpl监听器类的connectionClosed方法被触发。
2,判断同步还是异步的(默认是同步)。如果是异步,那么就采用线程池来进行处理回收,如果是同步,那么就
进行直接调用BasicResourcePool类的checkinResource方法对连接进行回收,此处的回收并不是销毁,而是归
还。
9,死锁机制
在C3P0中有很多任务都是采取多线程进行处理的。比如添加链接或者是回收连接。一旦有这种类似于生产者消费
者的多线程任务,那么就必须要进行死锁判断,否则会进入无线等待状态。
该机制的是由ThreadPoolAsynchronousRunner类的子类DeadlockDetector来执行具体的操作的。、
DeadlockDetector类继承了TimerTask类,由其中的Timer来调用DeadlockDetecto运行。DeadlockDetector类中
有两个属性:LinkedListlast 和 LinkedListcurrent。
这两个LinkedList集合放的是等待执行的任务。根据他的属性名字就很轻易理解,last是上次检测时待执行的任务
而current是本次检测时要执行的任务,当pendingTasks不为空且current.equals( last)时认为死锁发生。当一旦确
认死锁发生了,那么会把当前任务交给ThreadPerTaskAsynchronousRunner类来处理,这个类会启用新的线程
DispatchThread来处理当前的任务队列。
总结:
连接池可以在访问数据库时降低系统消耗。所以不管是什么项目都必定会有连接池的存在。
连接池基本使用非常的简单,通过一些简单的配置即可,但是想要真正把连接池原理理解,建议等用熟之后,闲暇
之余再进行研究。
|
|