DBCP数据源连接池实现原理分析

前些天在调试公司系统的时候发现这样的一个问题:mysql数据库服务停止一段时间后再次重启后吗,tomcat服务无法请求数据库服务,调试了半天对这个问题进行定位解决,期间也搞了很多有关mysql数据库的知识,包括数据库连接池的问题,以前没有遇到问题的时候只知道数据库连接池这个概念和如何配置,但是当遇到问题的时候就要去看怎么实现了,比如很简单的默认的数据库连接池的个数是多少呢,我相信没有看过源代码的是不知道的,答案是8.下面就针对最近学习的org.apache.commons.dbcp.BasicDataSource这个数据源的连接池做一个分享吧。

分享之前有关数据库连接池的一些概念性的问题就不解释,可以参考http://www.cnblogs.com/duanxiaojun/p/5413502.html

下面我们以一个完成的数据库操作来分析详细的dbcp数据源连接池实现的原理:

 1 package cn.com.chnsys.dbcpDataSource;
 2
 3 import javax.sql.DataSource;
 4 import java.sql.Connection;
 5 import java.sql.Statement;
 6 import java.sql.ResultSet;
 7 import java.sql.SQLException;
 8 import org.apache.commons.dbcp.BasicDataSource;
 9 /**
10  *
11  * <p>dbcp数据源连接池分析</p>
12  *
13  * 类说明
14  *
15  * @author duanxj
16  * @version 1.0
17  */
18 public class BasicDataSourceExample {
19
20     public static void main(String[] args) {
21         //设置数据源基本配置项
22         DataSource dataSource = setupDataSource(args[0]);
23         //创建连接
24         Connection conn = null;
25         Statement stmt = null;
26         ResultSet rset = null;
27         try {
28            //创建连接对象
29             conn = dataSource.getConnection();
30             //创建Statement 对象,这里我们使用Statement prepareStatement也是一样的
31             stmt = conn.createStatement();
32             //创建结果返回集
33             rset = stmt.executeQuery(args[1]);
34             //得到查询影响记录数
35             int numcols = rset.getMetaData().getColumnCount();
36             while(rset.next()) {
37                 for(int i=1;i<=numcols;i++) {
38                     System.out.print("\t" + rset.getString(i));
39                 }
40                 System.out.println("");
41             }
42         } catch(SQLException e) {
43             e.printStackTrace();
44         } finally {
45             //关闭资源
46             try { if (rset != null) rset.close(); } catch(Exception e) { }
47             try { if (stmt != null) stmt.close(); } catch(Exception e) { }
48             try { if (conn != null) conn.close(); } catch(Exception e) { }
49         }
50     }
51
52     /**
53      * 创建数据源,并设置数据源基本配置项
54      * @param connectURI
55      * @return
56      */
57     public static DataSource setupDataSource(String connectURI) {
58         BasicDataSource ds = new BasicDataSource();
59         ds.setDriverClassName("oracle.jdbc.driver.OracleDriver"); //设置驱动
60         ds.setUsername("root"); //设置用户名
61         ds.setPassword("root");  //设置密码
62         ds.setUrl(connectURI);//设置连接url
63         return ds;
64     }
65    /**
66     * 打印创建的数据源的配置项
67     * @param ds
68     */
69     public static void printDataSourceStats(DataSource ds) {
70         BasicDataSource bds = (BasicDataSource) ds;
71         System.out.println("NumActive: " + bds.getNumActive());
72         System.out.println("NumIdle: " + bds.getNumIdle());
73     }
74     /**
75      * 关闭销毁
76      * @param ds
77      * @throws SQLException
78      */
79     public static void shutdownDataSource(DataSource ds) throws SQLException {
80         BasicDataSource bds = (BasicDataSource) ds;
81         bds.close();
82     }
83 }  

上面的代码大家应该能看懂,我们只说其中比较关键的。首先哟啊创建一个dataSource,并对这个dataSource设置一些必须的配置项(数据库驱动,URL,用户名,密码)等,然后关键的操作是在dataSource.getConnection();处,我们查看dbcp源码的实现就可以知道,datasource的配置只是配置一些有关的配置信息,真正的创建连接池pool的操作是在用户第一次去获取connection的时候创建的,下面是源码getConnection()的实现:

public Connection getConnection()
    throws SQLException
  {
    return createDataSource().getConnection();
  }
 protected synchronized DataSource createDataSource()
    throws SQLException
  {
    if (this.closed) {
      throw new SQLException("Data source is closed");
    }

    if (this.dataSource != null) {
      return this.dataSource;
    }

    ConnectionFactory driverConnectionFactory = createConnectionFactory();

    createConnectionPool();

    GenericKeyedObjectPoolFactory statementPoolFactory = null;
    if (isPoolPreparedStatements()) {
      statementPoolFactory = new GenericKeyedObjectPoolFactory(null, -1, (byte)0, 0L, 1, this.maxOpenPreparedStatements);
    }

    createPoolableConnectionFactory(driverConnectionFactory, statementPoolFactory, this.abandonedConfig);

    createDataSourceInstance();
    try
    {
      for (int i = 0; i < this.initialSize; i++)
        this.connectionPool.addObject();
    }
    catch (Exception e) {
      throw new SQLNestedException("Error preloading the connection pool", e);
    }

    return this.dataSource;
  }

上面的代码我做如下的分析:

首先我们看到这个createDataSource()方法是用synchronized修饰的我们知道这是线程安全的。

1  首先是在getConnection()方法中先是去创建一个createDataSource()。在createDataSource(),的方法中先判断该数据源是否关闭,如果关闭直接抛出异常,如果这个数据源已经创建成功则直接返回,否则去创建这个数据源,

2 创建数据源之前先创建数据库的连接工厂createConnectionFactory();在这个方法中主要是通过反射CLASS.FORNAME()创建一个数据库连接对象,其中涉及到的参数主要是driverClassName,url,validationQuery,username,password(对这些链接数据库的配置应该不陌生,这些参数的作用就是创建一个连接工厂)。

ConnectionFactory driverConnectionFactory = new DriverConnectionFactory(driver, this.url, this.connectionProperties);并返回创建成功的连接工厂示例在下面创建连接池createConnectionPool使用。

3 下面是真正的创建连接池的核心部分createConnectionPool();我们先看一下这个方法的具体实现,这里的前提是我们已经创建成功一个与数据库的连接对象,

 1  protected void createConnectionPool()
 2   {
 3     GenericObjectPool gop;
 4     GenericObjectPool gop;
 5     if ((this.abandonedConfig != null) && (this.abandonedConfig.getRemoveAbandoned())) {
 6       gop = new AbandonedObjectPool(null, this.abandonedConfig);
 7     }
 8     else {
 9       gop = new GenericObjectPool();
10     }
11     gop.setMaxActive(this.maxActive);
12     gop.setMaxIdle(this.maxIdle);
13     gop.setMinIdle(this.minIdle);
14     gop.setMaxWait(this.maxWait);
15     gop.setTestOnBorrow(this.testOnBorrow);
16     gop.setTestOnReturn(this.testOnReturn);
17     gop.setTimeBetweenEvictionRunsMillis(this.timeBetweenEvictionRunsMillis);
18     gop.setNumTestsPerEvictionRun(this.numTestsPerEvictionRun);
19     gop.setMinEvictableIdleTimeMillis(this.minEvictableIdleTimeMillis);
20     gop.setTestWhileIdle(this.testWhileIdle);
21     this.connectionPool = gop;
22   }

在源码中我们可以看到使用了GenericObjectPool对象,这个是使用commons-pool的GenericObjectPool 实现的,创建一个GenericObjectPool对象并设置连接池的配置参数信息,(这里知道我们再配置文件中配置的选项是如何在源码中起作用的了吧~~)。然后返回这个GenericObjectPool(这个commons-pool的GenericObjectPool的源码后续我们再分析,大家只要知道这里是使用的commons-pool的GenericObjectPool创建了一个池 )。

3.1 创建成功  this.connectionPool后

就是创建GenericKeyedObjectPoolFactory(暂时不知道这个是个什么东东?????)

// 设置连接池工厂  createPoolableConnectionFactory(driverConnectionFactory, statementPoolFactory, abandonedConfig);

// 建立数据库连接池实例   createDataSourceInstance();

然后是根据我们配置文件中配置的初始的数据库连接池的大小去设置连接池的初始个数

// 根据配置,初始化建立一些数据库连接  =

try {

for (int i = 0 ; i < initialSize ; i++) {

connectionPool.addObject();

}

} catch (Exception e) {

throw new SQLNestedException("Error preloading the connection pool", e);

}

这样一个完成的数据源dataSource和连接池就创建完成了。

 

时间: 2024-10-25 15:46:49

DBCP数据源连接池实现原理分析的相关文章

《java数据源—连接池》

<java数据源-连接池>1.数据源的分类:直接数据源.连接池数据源.2.连接池.数据源.JNDI a.数据源:Java中的数据源就是连接到数据库的一条路径,数据源中并无真正的数据,它仅仅记录的是你连接到哪个数据库,以及如何连接. b.连接池:简单的说就是保存所有的数据库连接的地方,在系统初始化时,将数据库连接对象存储到内存里,当用户需要访问数据库的时候,并不是建立一个新的连接,而是从连接池中 取出一个已经建立好的空闲连接对象.而连接池负责分配.管理.释放数据库连接对象.注意的是:连接池是由容

spring配置数据源连接池

spring配置详解-连接池配置(转载) 一.连接池概述 数据库连接池概述: 数据库连接是一种关键的有限的昂贵的资源,这一点在多用户的网页应用程序中体现得尤为突出.对数据库连接的管理能显著影响到整个 应用程序的伸缩性和健壮性,影响到程序的性能指标.数据库连接池正是针对这个问题提出来的. 数据库连接池负责分配.管理和释放数据库连接,它允许应用程序重复使用一个现有的数据库连接,而再不是重新建立一个:释放空闲时 间超过最大空闲时间的数据库连接来避免因为没有释放数据库连接而引起的数据库连接遗漏.这项技术

python 里的 redis 连接池的原理

python设置redis连接池的好处: 通常情况下,需要连接redis时,会创建一个连接,基于这个连接进行redis操作,操作完成后去释放,正常情况下,这是没有问题的,但是并发量较高的情况下,频繁的连接创建和释放对性能会有较高的影响,于是连接池发挥作用. 连接池的原理:‘预先创建多个连接,当进行redis操作时,直接获取已经创建好的连接进行操作.完成后,不会释放这个连接,而是让其返回连接池,用于后续redis操作!这样避免连续创建和释放,从而提高了性能! import redis pool =

搭建spring工程配置数据源连接池

Spring作为一个优秀的开源框架,越来越为大家所熟知,前段时间用搭了个spring工程来管理数据库连接池,没有借助Eclipse纯手工搭建,网上此类文章不多,这里给大家分享一下,也作为一个手记. 工程结构: 注意:src目录下的applicationContext.xml是单元测试用的,作为web工程的话就使用WEB-INF下的applicationContext.xml. 1.下载jar包 这里需要下载很多spring的jar包,推荐去官网下载,很多人会说官网只提供maven和gradle下

dbcp,c3p0连接池

<!-- 配置dbcp数据源 --> <bean id="dataSource2" destroy-method="close" class="org.apache.commons.dbcp.BasicDataSource"> <property name="driverClassName" value="${jdbc.driverClassName}"/> <pr

记录一个简单的dbcp数据连接池

这个示例用到了ThreadLocal与dbcp,我觉得有点意思,就整理了下.使用dbcp,肯定要导入commons-dbcp.jar包.下面直接贴DBUtil代码: public class DBUtil { private static DataSource ds; //定义一个数据连接池 //threadLocal是线程的局部变量,它的实例通常是类中的 private static 字段 private static ThreadLocal<Connection> connLocal=ne

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

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

(转)Tomcat数据源连接池加密

文章来源 :http://my.oschina.net/cimu/blog/164757 我们在使用Tomcat数据库连接池的时候都是明文存储数据库用户名和密码的,例如: <Resource name="ODS" type="javax.sql.DataSource" driverClassName="oracle.jdbc.driver.OracleDriver" url="jdbc:oracle:thin:@192.168.1

Java 使用 DBCP mysql 连接池 做数据库操作

需要的jar包有 commons-dbutils , commons-dbcp , commons-pool , mysql-connector-java 本地database.propertties 配置为 driverClass=com.mysql.jdbc.Driver url=jdbc:mysql://localhost/mydatabase?useUnicode=true&characterEncoding=GBK username=root password=*** 该文件创建在sr