apache common pool

apache-common pool的使用

Apache commons-pool本质上是"对象池",即通过一定的规则来维护对象集合的容器;commos-pool在很多场景中,用来实现"连接池"/"任务worker池"等,大家常用的dbcp数据库连接池,也是基于commons-pool实现.

commons-pool实现思想非常简单,它主要的作用就是将"对象集合"池化,任何通过pool进行对象存取的操作,都会严格按照"pool配置"(比如池的大小)实时的创建对象/阻塞控制/销毁对象等.它在一定程度上,实现了对象集合的管理以及对象的分发.

1) 将创建对象的方式,使用工厂模式;

2) 通过"pool配置"来约束对象存取的时机

3) 将对象列表保存在队列中(LinkedList)

首选需要声明,不同的"对象池"(或者连接池)在设计上可能存在很大的区别,但是在思想上大同小异,本文主要讲解commons-pool,它和其他"连接池"的区别在此不多讨论.

一.对象生命周期

二.Config详解:

  1. maxActive: 链接池中最大连接数,默认为8.
  2. maxIdle: 链接池中最大空闲的连接数,默认为8.
  3. minIdle: 连接池中最少空闲的连接数,默认为0.
  4. maxWait: 当连接池资源耗尽时,调用者最大阻塞的时间,超时将跑出异常。单位,毫秒数;默认为-1.表示永不超时.
  5. minEvictableIdleTimeMillis: 连接空闲的最小时间,达到此值后空闲连接将可能会被移除。负值(-1)表示不移除。
  6. softMinEvictableIdleTimeMillis: 连接空闲的最小时间,达到此值后空闲链接将会被移除,且保留“minIdle”个空闲连接数。默认为-1.
  7. numTestsPerEvictionRun: 对于“空闲链接”检测线程而言,每次检测的链接资源的个数。默认为3.
  8. testOnBorrow: 向调用者输出“链接”资源时,是否检测是有有效,如果无效则从连接池中移除,并尝试获取继续获取。默认为false。建议保持默认值.
  9. testOnReturn:  向连接池“归还”链接时,是否检测“链接”对象的有效性。默认为false。建议保持默认值.
  10. testWhileIdle:  向调用者输出“链接”对象时,是否检测它的空闲超时;默认为false。如果“链接”空闲超时,将会被移除。建议保持默认值.
  11. timeBetweenEvictionRunsMillis:  “空闲链接”检测线程,检测的周期,毫秒数。如果为负值,表示不运行“检测线程”。默认为-1.
  12. whenExhaustedAction: 当“连接池”中active数量达到阀值时,即“链接”资源耗尽时,连接池需要采取的手段, 默认为1:
     -> 0 : 抛出异常,
     -> 1 : 阻塞,直到有可用链接资源
     -> 2 : 强制创建新的链接资源

这些属性均可以在org.apache.commons.pool.impl.GenericObjectPool.Config中进行设定。

三.原理解析

1) 对象池创建(参考GenericObjectPool):

  • public GenericObjectPool(PoolableObjectFactory factory, GenericObjectPool.Config config) :

    此方法创建一个GenericObjectPool实例,GenericObjectPool类已经实现了和对象池有关的所有核心操作,开发者可以通过继
    承或者封装的方式来使用它.通过此构造函数,我们能够清晰的看到,一个Pool中需要指定PoolableObjectFactory
    实例,以及此对象池的Config信息.PoolableObjectFactory主要用来"创建新对象",比如当对象池中的对象不足时,可以使用
    PoolableObjectFactory.makeObject()方法来创建对象,并交付给Pool管理.

此构造函数实例化了一个LinkedList作为"对象池"容器,用来存取"对象".此外还会根据timeBetweenEvictionRunsMillis的值来决定是否启动一个后台线程,此线程用来周期性扫描pool中的对象列表,已检测"对象池中的对象"空闲(idle)的时间是否达到了阀值,如果是,则移除此对象.

Java代码

  1. if ((getMinEvictableIdleTimeMillis() > 0) &&
  2. (idleTimeMilis > getMinEvictableIdleTimeMillis())) {
  3. removeObject = true;
  4. }
  5. ...
  6. if (removeObject) {
  7. try {
  8. _factory.destroyObject(pair.value);
  9. } catch(Exception e) {
  10. // ignored
  11. }
  12. }

2) 对象工厂PoolableObjectFactory接口:

commons-pool通过使用ObjectFactory(工厂模式)的方式将"对象池中的对象"的创建/检测/销毁等特性解耦出来,这是一个非常良
好的设计思想.此接口有一个抽象类BasePoolableObjectFactory,可供开发者继承和实现.

  • Object makeObject() : 创建一个新对象;当对象池中的对象个数不足时,将会使用此方法来"输出"一个新的"对象",并交付给对象池管理.
  • void destroyObject(Object obj) :

    销毁对象,如果对象池中检测到某个"对象"idle的时间超时,或者操作者向对象池"归还对象"时检测到"对象"已经无效,那么此时将会导致"对象销
    毁";"销毁对象"的操作设计相差甚远,但是必须明确:当调用此方法时,"对象"的生命周期必须结束.如果object是线程,那么此时线程必须退出;如
    果object是socket操作,那么此时socket必须关闭;如果object是文件流操作,那么此时"数据flush"且正常关闭.
  • boolean validateObject(Object obj) :

    检测对象是否"有效";Pool中不能保存无效的"对象",因此"后台检测线程"会周期性的检测Pool中"对象"的有效性,如果对象无效则会导致此对象
    从Pool中移除,并destroy;此外在调用者从Pool获取一个"对象"时,也会检测"对象"的有效性,确保不能讲"无效"的对象输出给调用者;当
    调用者使用完毕将"对象归还"到Pool时,仍然会检测对象的有效性.所谓有效性,就是此"对象"的状态是否符合预期,是否可以对调用者直接使用;如果对
    象是Socket,那么它的有效性就是socket的通道是否畅通/阻塞是否超时等.

  • void activateObject(Object obj) :

    "激活"对象,当Pool中决定移除一个对象交付给调用者时额外的"激活"操作,比如可以在activateObject方法中"重置"参数列表让调用者
    使用时感觉像一个"新创建"的对象一样;如果object是一个线程,可以在"激活"操作中重置"线程中断标记",或者让线程从阻塞中唤醒等;如果
    object是一个socket,那么可以在"激活操作"中刷新通道,或者对socket进行链接重建(假如socket意外关闭)等.

  • void void passivateObject(Object obj) :
    "钝化"对象,当调用者"归还对象"时,Pool将会"钝化对象";钝化的言外之意,就是此"对象"暂且需要"休息"一下.如果object是一个
    socket,那么可以passivateObject中清除buffer,将socket阻塞;如果object是一个线程,可以在"钝化"操作中将线
    程sleep或者将线程中的某个对象wait.需要注意的时,activateObject和passivateObject两个方法需要对应,避免死锁
    或者"对象"状态的混乱.

 3) ObjectPool接口与实现:

对象池是commons-pool的核心接口,用来维护"对象列表"的存取;其中GenericObjectPool是其实现类,它已经实现了相关的功能.

  • Object borrowObject() : 从Pool获取一个对象,此操作将导致一个"对象"从Pool移除(脱离Pool管理),调用者可以在获得"对象"引用后即可使用,且需要在使用结束后"归还".如下为伪代码:

Java代码

  1. public Object borrowObject() throws Exception {
  2. Object value = null;
  3. synchronized (this) {
  4. if(!_pool.isEmpty()){
  5. value = _pool.remove();
  6. }
  7. }
  8. for(;;) {
  9. //如果Pool中没有"对象",则根据相应的"耗尽"策略
  10. if(value == null) {
  11. switch(whenExhaustedAction) {
  12. //如果耗尽,仍继续创建新"对象"
  13. case WHEN_EXHAUSTED_GROW:
  14. value = _factory.makeObject();
  15. break;
  16. //如果耗尽,则终止,此时以异常的方式退出.
  17. case WHEN_EXHAUSTED_FAIL:
  18. throw new NoSuchElementException("Pool exhausted");
  19. //如果耗尽,则阻塞,直到有"对象"归还
  20. case WHEN_EXHAUSTED_BLOCK:
  21. try {
  22. synchronized (value) {
  23. if (value == null) {
  24. //maxWait为Config中指定的"最大等待时间"
  25. if(maxWait <= 0) {
  26. latch.wait();
  27. } else {
  28. latch.wait(waitTime);
  29. }
  30. } else {
  31. break;
  32. }
  33. }
  34. } catch(InterruptedException e) {
  35. //
  36. break;
  37. }
  38. default://
  39. }
  40. }
  41. try {
  42. _factory.activateObject(latch.getPair().value);
  43. if(_testOnBorrow &&
  44. !_factory.validateObject(latch.getPair().value)) {
  45. throw new Exception("ValidateObject failed");
  46. }
  47. return value;
  48. }
  49. catch (Throwable e) {
  50. try {
  51. _factory.destroyObject(latch.getPair().value);
  52. } catch (Throwable e2) {
  53. //
  54. }
  55. }
  56. }
  57. }
  • void returnObject(Object obj) : "归还"对象,当"对象"使用结束后,需要归还到Pool中,才能维持Pool中对象的数量可控,如果不归还到Pool,那么将意味着在Pool之外,将有大量的"对象"存在,那么就使用了"对象池"的意义.如下为伪代码:

Java代码

  1. public void returnObject(Object obj) throws Exception {
  2. try {
  3. boolean success = true;//
  4. if(_testOnReturn && !(_factory.validateObject(obj))) {
  5. success = false;
  6. } else {
  7. _factory.passivateObject(obj);
  8. }
  9. synchronized (this) {
  10. //检测pool中已经空闲的对象个数是否达到阀值.
  11. if((_maxIdle >= 0) && (_pool.size() >= _maxIdle)) {
  12. success = false;
  13. } else if(success) {
  14. _pool.addFirst(new ObjectTimestampPair(obj));
  15. }
  16. }
  17. // Destroy the instance if necessary
  18. if(!success) {
  19. try {
  20. _factory.destroyObject(obj);
  21. } catch(Exception e) {
  22. // ignored
  23. }
  24. }
  25. } catch (Exception e) {
  26. //
  27. }
  28. }
  • void invalidateObject(Object obj) : 销毁对象,直接调用ObjectFactory.destroyObject(obj);.
  • void addObject() : 开发者可以直接调用addObject方法用于直接创建一个"对象"并添加到Pool中.

四.代码实例.

本实例主要用来演示一个"TCP连接池".

1) ConnectionPoolFactory.java:

Java代码

  1. import org.apache.commons.pool.BasePoolableObjectFactory;
  2. import org.apache.commons.pool.impl.GenericObjectPool;
  3. import org.apache.commons.pool.impl.GenericObjectPool.Config;
  4. public class ConnectionPoolFactory {
  5. private GenericObjectPool pool;
  6. public ConnectionPoolFactory(Config config,String ip,int port){
  7. ConnectionFactory factory = new ConnectionFactory(ip, port);
  8. pool = new GenericObjectPool(factory, config);
  9. }
  10. public Socket getConnection() throws Exception{
  11. return (Socket)pool.borrowObject();
  12. }
  13. public void releaseConnection(Socket socket){
  14. try{
  15. pool.returnObject(socket);
  16. }catch(Exception e){
  17. if(socket != null){
  18. try{
  19. socket.close();
  20. }catch(Exception ex){
  21. //
  22. }
  23. }
  24. }
  25. }
  26. /**
  27. * inner
  28. * @author qing
  29. *
  30. */
  31. class ConnectionFactory extends BasePoolableObjectFactory {
  32. private InetSocketAddress address;
  33. public ConnectionFactory(String ip,int port){
  34. address = new InetSocketAddress(ip, port);
  35. }
  36. @Override
  37. public Object makeObject() throws Exception {
  38. Socket socket = new Socket();
  39. socket.connect(address);
  40. return socket;
  41. }
  42. public void destroyObject(Object obj) throws Exception  {
  43. if(obj instanceof Socket){
  44. ((Socket)obj).close();
  45. }
  46. }
  47. public boolean validateObject(Object obj) {
  48. if(obj instanceof Socket){
  49. Socket socket = ((Socket)obj);
  50. if(!socket.isConnected()){
  51. return false;
  52. }
  53. if(socket.isClosed()){
  54. return false;
  55. }
  56. return true;
  57. }
  58. return false;
  59. }
  60. }
  61. }

2) TestMain.java(测试类):

Java代码

    1. public class TestMain {
    2. /**
    3. * @param args
    4. */
    5. public static void main(String[] args) {
    6. Config config = new Config();
    7. config.maxActive = 16;
    8. config.maxWait = 30000;
    9. ConnectionPoolFactory poolFactory = new ConnectionPoolFactory(config, "127.0.0.1", 8011);
    10. Socket socket = null ;
    11. try{
    12. socket = poolFactory.getConnection();
    13. ////
    14. }catch(Exception e){
    15. e.printStackTrace();
    16. }finally{
    17. if(socket != null){
    18. poolFactory.releaseConnection(socket);
    19. }
    20. }
    21. }
    22. }

commons-pool-1.6-bin.tar.gz

时间: 2024-10-02 23:13:56

apache common pool的相关文章

apache common email组件小结

apache common email组件小结.这是个很好的玩意,可以支持单发简单邮件,发附件,发HTML格式邮件,小结如下: import java.io.File;import java.io.UnsupportedEncodingException;import java.net.MalformedURLException;import java.net.URL;import javax.mail.internet.MimeUtility;import org.apache.commons

Cache Lucene IndexReader with Apache Commons Pool

IndexReaderFactory.java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 package org.ostree.module.lucene; import org.apache.commons.pool.Ke

apache commons pool

apache commons下的pool 其中的borrowObject函数源代码显示其产生可用对象的过程: 如果stack中有空闲的对象,则pop对象,激活对象(activate函数),验证对象(validate函数).最终将合格的对象返回给client. 若对象在这个流程中出错,则在从stack中取出一个,并执行相同的流程.如此循环,直到stack为空. 如果stack为空,则直接调用makeObject函数创建一个对象.在返回对象之前,还会调用验证函数(validate)验证是否有效. 转

Apache Commons pool 简介和pool连接池代码

在实际中工作,我们经常遇到需要连接池的地方,特别是数据库连接池. 我们为什么需要池呢?因为这些资源的创建,都很消耗资源.因此,我们使用一个对象池,里面预先创建了一些资源对象.当我们需要时,从池中取出对象,而不需要时,把对象返回池中.这样就可以提高代码运行的效率. Apache Commons Pool(http://commons.apache.org/pool/)为我们提供了很方便的接口来实现对象池.我们唯一需要实现的就是如何产生对象,而不用去考虑一堆多线程问题. 2013年,Apache C

Apache common pool2 对象池

对象池的容器:包含一个指定数量的对象.从池中取出一个对象时,它就不存在池中,直到它被放回.在池中的对象有生命周期:创建,验证,销毁,对象池有助于更好地管理可用资源,防止JVM内部大量临时小对象,频繁触发垃圾回收,造成系统暂停.有许多的使用示例.特别是在应用服务器数据源池,线程池等都是对象池的使用,下面情况适合使用对象池: 同样的对象高频率使用 对象太大消耗很多内存 对象初始化需要时间 对象内涉及IO操作 (Streams, Sockets, DB, etc.) 对象并不是线程安全时. 很多人使用

The type org.apache.commons.pool.impl.GenericObjectPool$Config cannot be resolved. It is indirectly

static { try { JedisPoolConfig config = new JedisPoolConfig(); config.setMaxActive(MAX_ACTIVE); config.setMaxIdle(MAX_IDLE); config.setMaxWait(MAX_WAIT); config.setTestOnBorrow(TEST_ON_BORROW); jedisPool = new JedisPool(config, ADDR, PORT, TIMEOUT, A

Android Apache common ftp开源库以及http区别分析

1.前言: ftp开源库:Apache common ftp开源库上传文件到局域网的ftp上吧.开源库是commons-net-2.2.jar.包名是这样的:org.apache.commons.net.ftp.FTPClient;用这个框架也能可以上传,下载以及删除ftp服务器的文件的.我也是参考网上大神例子迅速在项目中使用,现在趁机会总结一下,以及我自已在此基础上再次封装的ftp使用类. http开源库:之前开发的时候先是用到了http协议上传文件,删除文件等等,使用的开源库是AsyncHt

Android中使用Apache common ftp进行下载文件

在Android使用ftp下载资源 可以使用ftp4j组件,还可以用apache common net里面的ftp组件,这2个组件我都用过. 个人感觉Apache common net里面的组件比较好用一些,下面是一个实例. 项目中对ftp的使用进行了封装,添加了回调函数已经断点续传的方法. FTPCfg 用来存储ftp地址,密码等信息的. FTPClientProxy 只是个代理而已,里面主要封装了common ftp api IRetrieveListener做回调用的,比如用于是否下载完成

Apache Common DbUtils

前段时间使用了Apache Common DbUtils这个工具,在此留个印,以备不时查看.大家都知道现在市面上的数据库访问层的框架很多,当然很多都是包含了OR-Mapping工作步骤的 例如大家常用的Hibernate与Mybatis.当然如果人们要一个纯粹的封装了JDBC的工具类,使用Apache Common DbUtils(下面简称ACD)是个不错的选择,这个工具在JDBC的基础上稍加封装是JDBC的操作更加便捷,在学习使用这个框架的途中你也不需要学 习太多的API类,因为一共也才3个部