Tomcat JDBC pool源码部析
最近跟随Tomcat7.0开发了一个JDBC 连接池。Svn: http://svn.apache.org/repos/asf/tomcat/trunk/modules/jdbc-pool
http://tomcat.apache.org/tomcat-7.0-doc/jdbc-pool.html上大篇幅的介绍,包括基本的使用指南。本篇从源码的角度,分析该连接池的实现思路。
应用使用JDBC连接也,主要关注以下三个方面:
1.获取连接
2.归还连接
3.空闲连接关闭。
一. 获取连接
ConnectionPool提供三个接口用于获取连接:
[*]public Future getConnectionAsync() throws SQLException {
[*]
[*] try {
[*]
[*] PooledConnection pc = borrowConnection(0, null, null);
[*]
[*] if (pc!=null) {
[*]
[*] return new ConnectionFuture(pc);
[*]
[*] }
[*]
[*] }catch (SQLException x) {
[*]
[*] if (x.getMessage().indexOf("NoWait")) {
[*]
[*] Future pcf = ((MultiLockFairBlockingQueue)idle).pollAsync();
[*]
[*] return new ConnectionFuture(pcf);
[*]
[*] } else {
[*]
[*] throw new SQLException("Connection pool is misconfigured, doesn't support async retrieval. Set the 'fair' property to 'true'");
[*]
[*] }
[*]
[*]
[*]
[*]public Connection getConnection() throws SQLException {
[*]
[*] //check out a connection
[*]
[*] PooledConnection con = borrowConnection(-1,null,null);
[*]
[*] return setupConnection(con);
[*]
[*] }
[*]
[*]public Connection getConnection(String username, String password) throws SQLException {
[*]
[*] // check out a connection
[*]
[*] PooledConnection con = borrowConnection(-1, username, password);
[*]
[*] return setupConnection(con);
[*]
[*] }
第一个方法:getConnectionAsync用于获取一个连接的Feature.它用于支持以异步的方式获取连接。后两个方法不同之处就是传递了所需连接的用户名与密码。我们这里得点分析第三个方法.
PooledConnection con = borrowConnection(-1, username, password);
borrowConnection方法从空闲队列中获取一个连接,或新建一个连接。看一下源码:
[*]/**
[*]
[*] * Thread safe way to retrieve a connection from the pool
[*]
[*]* @param wait - time to wait, overrides the maxWait from the properties,
[*]
[*]* set to -1 if you wish to use maxWait, 0 if you wish no wait time.
[*]
[*] * @return PooledConnection
[*]
[*] * @throws SQLException
[*]
[*] */
[*]
[*]private PooledConnection borrowConnection(int wait, String username, String password) throws SQLException {
[*]
[*] //如果连接被关闭则直接抛出异常
[*]
[*] if (isClosed()) {
[*]
[*] throw new SQLException("Connection pool closed.");
[*]
[*] } //end if
[*]
[*]
[*]
[*] //get the current time stamp
[*]
[*] long now = System.currentTimeMillis();
[*]
[*] //see if there is one available immediately
[*]
[*] /*从空闲队列中获取一个连接。 其实idle里存在连接对象有的可能并没有
[*]
[*]绑定物理连接。这也是Tomcat jdbc pool的一个特别,连接在将要被使用时,
[*]
[*]才会初始化*/
[*]
[*]PooledConnection con = idle.poll();
[*]
[*] while (true) {
[*]
[*] if (con!=null) {
[*]
[*] //configure the connection and return it
[*]
[*] /*这里又出现一个borrowConnection的重载方法。该方法对从空闲队列中取到的连接对象进行配置和验证,稍后评述*/
[*]
[*] PooledConnection result = borrowConnection(now, con, username, password);
[*]
[*] //null should never be returned, but was in a previous impl.
[*]
[*] // null should never be returned这句注释不对,根据后面的代码
[*]
[*] // 来看,null是有可能发生。
[*]
[*] if (result!=null) return result;
[*]
[*] }
[*]
[*] //if we get here, see if we need to create one
[*]
[*] //this is not 100% accurate since it doesn't use a shared
[*]
[*] //atomic variable - a connection can become idle while we are creating
[*]
[*] //a new connection
[*]
[*] /*从上面的英文注释我们很明白,当执行到这里时,唯一的可能是idle队列没能取到连接对象。
[*] 如果条件允许,我们将创建新的连接.在这里作者用了一个特别的算法,也是tomcat代码中常用的,
[*] 我们姑且称他占位法(我一般这么叫)。这个算法的特点就是先在计数器Size中占一个位置
[*] (Size是原子变量。能够解决并发问题)。即size+1.然后检查size有没有超标。如果超标
[*] 则减去刚才新加的1。否则创建一个新的连接。不过这里我注意的是,如果创建新连接时失败,
[*] size也必须减1。其实与大学时的用书抢位子异曲同工。*/
[*]
[*] if (size.get() < getPoolProperties().getMaxActive()) {
[*]
[*] //atomic duplicate check
[*]
[*] if (size.addAndGet(1) > getPoolProperties().getMaxActive()) {
[*]
[*] //if we got here, two threads passed through the first if
[*]
[*] size.decrementAndGet();
[*]
[*] } else {
[*]
[*] //create a connection, we're below the limit
[*]
[*] //后面再描述这个方法。
[*]
[*] return createConnection(now, con, username, password);
[*]
[*] }
[*]
[*] } //end if
[*]
[*] //到这里则表示连接池已满,不能创建新的连接,我们只能等待其他线程释放的连接
[*]
[*] //calculate wait time for this iteration
[*]
[*] long maxWait = wait;
[*]
[*] //if the passed in wait time is -1,
[*]
[*] //means we should use the pool property value
[*]
[*] if (wait==-1) {
[*]
[*] maxWait = (getPoolProperties().getMaxWait()
页:
[1]