文档章节

dbcp中几个重要实现类之间的关系和连接池参数简介

trayvon
 trayvon
发布于 2016/06/13 21:05
字数 1832
阅读 307
收藏 0

  如下图1所示主要分析的是BasicDataSourceGenericObjectPoolDriverConnectionFactoryPoolableConnectionFactoryPoolingDataSource类。

1  dbcp几个重要实现类的关系

   我们直接使用的最多的就是BasicDataSource类了,这个类主要是设置一些数据库连接池的参数,不过这些参数基本都是通过GenericObjectPool的实例来实现的。

   在BasicDataSource中最重要的方法就是createDataSource方法了,这个方法中回创建一个GenericObjectPool实例来管理数据库连接(AbandonedObjectPool类已经不被推荐了,所以这里不考虑),最后这个方法返回的是PoolingDataSource的实例,这个类主要是对GenericObjectPool和数据库连接的一些代理实现。下面是createDataSource方法的实现。

protected synchronized DataSource createDataSource()
            throws SQLException {

            // 如果已经创建了DataSource则直接返回
            if (dataSource != null) {
                return (dataSource);
            }

            // 加载 数据库驱动类
            if (driverClassName != null) {
                try {
                    Class.forName(driverClassName);
                } catch (Throwable t) {
                    String message = "Cannot load JDBC driver class '" +
                        driverClassName + "'";
                    logWriter.println(message);
                    t.printStackTrace(logWriter);
                    throw new SQLNestedException(message, t);
                }
            }

            // 得到一个数据库驱动的实例 
            Driver driver = null;
            try {
                driver = DriverManager.getDriver(url);
            } catch (Throwable t) {
                String message = "Cannot create JDBC driver of class '" +
                    (driverClassName != null ? driverClassName : "") + 
                    "' for connect URL '" + url + "'";
                logWriter.println(message);
                t.printStackTrace(logWriter);
                throw new SQLNestedException(message, t);
            }

            // 如果没有配置(设置)validationQuery参数,则不执行相应的测试
            //下面的3个方法都是测试链接是否有效的
            //setTestOnBorrow方法是在获得连接的时候测试链接时候有效
            //setTestOnReturn是在数据库连接池使用完连接把它放入空闲链表中的时候测试连接是否有效
            //setTestWhileIdle是在驱逐超时的空闲连接的时候测试链接是否有效
            if (validationQuery == null) {
                setTestOnBorrow(false);
                setTestOnReturn(false);
                setTestWhileIdle(false);
            }

            // 如果配置了abandonedConfig 相应的参数则使用AbandonedObjectPool,则是是不推荐的
            if ((abandonedConfig != null) && (abandonedConfig.getRemoveAbandoned())) {
                connectionPool = new AbandonedObjectPool(null,abandonedConfig);
            }
            //所以一般使用的是GenericObjectPool类,这个类的主要工作是管理数据库连接池相关的配置
            //例如获取数据库连接,保证数据库连接池中的的空闲链接,驱逐空闲连接,
            //就是和数据库连接池相关的配置都由它管理实现,它为很多配置提供了默认值
            else {
                connectionPool = new GenericObjectPool();
            }
            connectionPool.setMaxActive(maxActive);
            connectionPool.setMaxIdle(maxIdle);
            connectionPool.setMinIdle(minIdle);
            connectionPool.setMaxWait(maxWait);
            connectionPool.setTestOnBorrow(testOnBorrow);
            connectionPool.setTestOnReturn(testOnReturn);
            connectionPool.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis);
            connectionPool.setNumTestsPerEvictionRun(numTestsPerEvictionRun);
            connectionPool.setMinEvictableIdleTimeMillis(minEvictableIdleTimeMillis);
            connectionPool.setTestWhileIdle(testWhileIdle);
            
            
            GenericKeyedObjectPoolFactory statementPoolFactory = null;
            if (isPoolPreparedStatements()) {
                statementPoolFactory = new GenericKeyedObjectPoolFactory(null, 
                            -1, // unlimited maxActive (per key)
                            GenericKeyedObjectPool.WHEN_EXHAUSTED_FAIL, 
                            0, // maxWait
                            1, // maxIdle (per key) 
                            maxOpenPreparedStatements); 
            }

            
            if (username != null) {
                connectionProperties.put("user", username);
            } else {
                log("DBCP DataSource configured without a 'username'");
            }
            
            if (password != null) {
                connectionProperties.put("password", password);
            } else {
                log("DBCP DataSource configured without a 'password'");
            }
            
            //这个是实际获得数据库连接的工厂类
            DriverConnectionFactory driverConnectionFactory =
                new DriverConnectionFactory(driver, url, connectionProperties);

            // 这个类实现了PoolableObjectFactory,它做的是封装一个DriverConnectionFactory
            //来得到实际的连接,封装了一个GenericObjectPool来管理连接池,当然都是针对接口的
            //重要的一点就是它把自身传递到了GenericObjectPool中,这样就只需要暴露GenericObjectPool
            PoolableConnectionFactory connectionFactory = null;
            try {
                connectionFactory =
                    new PoolableConnectionFactory(driverConnectionFactory,
                                                  connectionPool,
                                                  statementPoolFactory,
                                                  validationQuery,
                                                  defaultReadOnly,
                                                  defaultAutoCommit,
                                                  defaultTransactionIsolation,
                                                  defaultCatalog,
                                                  abandonedConfig);
                if (connectionFactory == null) {
                    throw new SQLException("Cannot create PoolableConnectionFactory");
                }
                validateConnectionFactory(connectionFactory);
            } catch (RuntimeException e) {
                throw e;
            } catch (Exception e) {
                throw new SQLNestedException("Cannot create PoolableConnectionFactory (" + e.getMessage() + ")", e);
            }

            // Create and return the pooling data source to manage the connections
            dataSource = new PoolingDataSource(connectionPool);
            ((PoolingDataSource) dataSource).setAccessToUnderlyingConnectionAllowed(isAccessToUnderlyingConnectionAllowed());
            dataSource.setLogWriter(logWriter);
            
            try {
                for (int i = 0 ; i < initialSize ; i++) {
                    connectionPool.addObject();
                }
            } catch (Exception e) {
                throw new SQLNestedException("Error preloading the connection pool", e);
            }
            
            return dataSource;
        }

 

      GenericObjectPool类主要是管理数据库连接池的,它主要实现了对数据库连接池参数的管理,例如连接池的最大数,它的创建连接时通过PoolableConnectionFactory的实例来实现的,不过它做了代理在创建连接的时候先检查了数据库的活动连接是不是已经达到了最大值,如果没有就继续创建,如果达到了参数设置的最大值就按指定的策略执行,默认的策略是当数据库连接池中的连接达到的最大值是在创建连接就会被阻塞(wait),这就是在上一篇中程序被挂起的主要原因。GenericObjectPool还可以检查最小的空闲连接,最大的空闲连接,驱逐超时的空闲连接。下面将数据库连接池参数的时候再介绍。

    DriverConnectionFactory是实际获取数据库连接的类,它调用的是底层的数据库驱动。

     PoolableConnectionFactory主要实现了创建连接,关闭连接,验证连接,使连接钝化(关闭Statement,设置关闭标志,不是真的关闭),激活连接等操作。GenericObjectPool关于连接的操作基本就是通过它实现的。

2 dbcp数据库连接池参数

   如上图2所示是dbcp数据库连接池的一些重要的参数,除了重要部分所示的usernamepasswordurldriverClassName参数使用BasicDataSource直接管理,其他基本都是通过GenericObjectPool来管理的。

   这里只是介绍一些比较容易混淆,不易理解的参数。首先看基本的initialSize是表示数据库连接池初始化的时候创建多少个连接。maxTotal是数据库连接池中最多创建多少个连接,maxIdelminIdel是指最大空闲连接和最小空闲连接的数量,空闲连接是指使用过后的连接关闭连接(并没有真正关闭连接,而是加入了GenericObjectPool空闲连接列表的数量(假设使用的是GenericObjectPool))。maxWaitMills参数是指当数据库连接池中的连接达到最大值的时候,被阻塞等待的时间(wait(maxWaitMills)),超时就会抛出异常。

   验证连接有效性的4个参数:首先必须得设置validationQuery参数,这个参数就是验证连接有效性的SQL语句,可以设置为select 1 from dual。只有设置了这个参数其他的三个参数才会生效。这个从createDataSource方法的代码中可以轻易的看出来。testOnBorrow是指在获取连接的时候检查连接的有效性,testOnReturn是指在关闭连接(不是真正关闭,而是加入到空闲连接列表的时候)验证连接的有效性。testOnIdle是指在驱逐空闲连接的时候验证连接的有效性。

   驱逐连接相关的3个参数,timeBetweenEvictionRunsMills参数是值多长时间执行一次驱逐算法minEvictableIdleMills参数是指空闲连接的超时时间(就是这个连接有多久没有用就可以被回收了)numTestsPerEvictionRun参数是值设置几个线程来执行驱逐算法。这个算法其实不是特别复杂,只是有一个Timer计时器,在构造的时候启动Evictor,其中Evictor实现了TimerTask。调用了GenericObjectPool public synchronized void evict() throws Exception方法。因为每一个连接在创建的时候又一个时间戳,对比一下就可以了。

   至于自动回收泄露连接的那几个参数,因为类已经不被推荐了所以也没有仔细看,就不介绍了。

记一次dbcp数据库连接池问题分析

© 著作权归作者所有

trayvon
粉丝 16
博文 140
码字总数 212221
作品 1
程序员
私信 提问
数据库连接池

这两天公司的事情不忙,看了一下ssh的框架,在看spring框架的时候看到数据源这一块。就想知道数据源这一块是怎么实现的,以前也有学习过数据源,但感觉没那么系统。在网上看到一个详细介绍数...

zdatbit
2016/05/12
98
0
Javaweb配置最全的数据源配置

DBCP DBCP是Apache推出的数据库连接池(Database Connection Pool)。 操作步骤: 添加jar包: commons-dbcp-1.4.jar commons-pool-1.5.6.jar 添加属性资源文件 dbcpconfig.properties文件。 ...

---dgw博客
2017/10/18
0
0
DBCP数据库连接失效的解决方法(Io 异常:Connection reset)

网上很多评论说DBCP有很多BUG,但是都没有指明是什么BUG,只有一部分人说数据库如果因为某种原因断掉后再DBCP取道的连接都是失效的连接,而没有重新取。有的时候会报Io 异常:Connection re...

SANSOM
2013/08/01
3.9K
0
连接池详解,c3p0与dbcp的区别!

连接池: 连接池是创建和管理一个连接的缓冲池的技术,这些连接准备好被任何需要它们的线程使用。这项技术能明显提高对数据库操作的性能。 连接池的好处: (1)对于大多数应用程序,当它们正...

IT_laobai
2018/06/20
0
0
proxool配置文件中用户和密码加密

Proxool是一种Java数据库连接池技术。sourceforge下的一个开源项目,这个项目提供一个健壮、易用的连接池,最为关键的是这个连接池提供监控的功能,方便易用,便于发现连接泄漏的情况。 Prox...

yunlielai
2017/06/23
0
0

没有更多内容

加载失败,请刷新页面

加载更多

手写RPC框架指北另送贴心注释代码一套

Angular8正式发布了,Java13再过几个月也要发布了,技术迭代这么快,框架的复杂度越来越大,但是原理是基本不变的。所以沉下心看清代码本质很重要,这次给大家带来的是手写RPC框架。 完整代码...

全菜工程师小辉
8分钟前
2
0
【Java】开发收货

简介 谨以此篇,记载开发过程中的一些tips。 编译器 【Shift + F6】可实现变量的联动修改。

Areya
24分钟前
5
0
DOM官方定义

DOM Document Object Model 文档对象模型 DOM的官方定义:W3C的DOM,可以使程序或者脚本(JS或AS\JScript),动态的访问或者操作文档的内容、结构、样式。 DOM只是一个标准,操作网页的标准。...

前端老手
30分钟前
6
0
IT兄弟连 HTML5教程 HTML5的学习线路图 第一阶段学习网页制作

学习HTML5技术可并不是简单学会几个新增的标签而已,HTML5现在可以说是前端所有技术的代名词。需要学习的语言和工具不仅多,对于刚接触他们的新人会感觉很乱。另外,前端开发也会细分很多个开...

老码农的一亩三分地
32分钟前
6
0
可见性有序性,Happens-before来搞定

写在前面 上一篇文章并发 Bug 之源有三,请睁大眼睛看清它们 谈到了可见性/原子性/有序性三个问题,这些问题通常违背我们的直觉和思考模式,也就导致了很多并发 Bug 为了解决 CPU,内存,IO ...

tan日拱一兵
47分钟前
4
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部