我们在服务器开发的过程中,往往会有一些对象,它的创建和初始化需要的时间比较长,比如数据库连接,网络IO,大数据对象等。在大量使用这些对象时,如果不采用一些技术优化,就会造成一些不可忽略的性能影响。一种办法就是使用对象池,每次创建的对象并不实际销毁,而是缓存在对象池中,下次使用的时候,不用再重新创建,直接从对象池的缓存中取即可。为了避免重新造轮子,我们可以使用优秀的开源对象池化组件apache-common-pool2,它对对象池化操作进行了很好的封装,我们只需要根据自己的业务需求重写或实现部分接口即可,使用它可以快速的创建一个方便,简单,强大对象连接池管理类。

一,common-pool2简介

首先是下载这个组件,使用maven引入下面依赖即可:

<!-- https://mvnrepository.com/artifact/org.apache.commons/commons-pool2 --><dependency><groupId>org.apache.commons</groupId><artifactId>commons-pool2</artifactId><version>2.4.2</version>
</dependency>

Common-pool2中的代码不是太多,有几个种要的接口和实现类,common-pool2使用的是面向接口的编程,它为我们提供的是一个抽象的对象池管理方式,所以根据我们业务的不同,我们需要重写或实现一些方法和接口,我们一个一个看一下。

1,GenericObjectPool

这个是对象池实现的核心类,它实现了对对象池的管理,是一个基本的对象池实现,一般情况下,我们可以直接使用。在使用这个类的时候,我们需要传入两个重要的参数:GenericObjectPoolConfig类和PooledObjectFactory接口的实现,一会我们再详细说这两个。

在GenericObjectPool中,有两个我们会用到的方法:

public T borrowObject() throws Exception 从对象池中获取一个对象

public void returnObject(T obj) 对象使用完之后,归还到对象池,

其它还有一些方法,比如关闭对象池,销毁对象池,获取对象池中空闲的对象个数等,可以自行查看API。

2,PooledObjectFactory接口

这个接口是我们要实现的,它对要实现对象池化的对象做了一些管理。这个工厂接口就是为了让我们根据自己的业务创建和管理要对象池化的对象。

PooledObject makeObject() throws Exception;

这个方法是用来创建一个对象,当在GenericObjectPool类中调用borrowObject方法时,如果当前对象池中没有空闲的对象,GenericObjectPool会调用这个方法,创建一个对象,并把这个对象封装到PooledObject类中,并交给对象池管理。

void destroyObject(PooledObject p) throws Exception;

销毁对象,当对象池检测到某个对象的空闲时间(idle)超时,或使用完对象归还到对象池之前被检测到对象已经无效时,就会调用这个方法销毁对象。对象的销毁一般和业务相关,但必须明确的是,当调用这个方法之后,对象的生命周期必须结果。如果是对象是线程,线程必须已结束,如果是socket,socket必须已close,如果是文件操作,文件数据必须已flush,且文件正常关闭。

boolean validateObject(PooledObject p);

检测一个对象是否有效。在对象池中的对象必须是有效的,这个有效的概念是,从对象池中拿出的对象是可用的。比如,如果是socket,那么必须保证socket是连接可用的。在从对象池获取对象或归还对象到对象池时,会调用这个方法,判断对象是否有效,如果无效就会销毁。

void activateObject(PooledObject p) throws Exception;

激活一个对象或者说启动对象的某些操作。比如,如果对象是socket,如果socket没有连接,或意外断开了,可以在这里启动socket的连接。它会在检测空闲对象的时候,如果设置了测试空闲对象是否可以用,就会调用这个方法,在borrowObject的时候也会调用。另外,如果对象是一个包含参数的对象,可以在这里进行初始化。让使用者感觉这是一个新创建的对象一样。

void passivateObject(PooledObject p) throws Exception;

钝化一个对象。在向对象池归还一个对象是会调用这个方法。这里可以对对象做一些清理操作。比如清理掉过期的数据,下次获得对象时,不受旧数据的影响。

一般来说activateObject和passivateObject是成对出现的。前者是在对象从对象池取出时做一些操作,后者是在对象归还到对象池做一些操作,可以根据自己的业务需要进行取舍。

3,带Key的对象池GenericKeyedObjectPool

这种对象池和前面的GenericObjectPool对象池操作是一样的,不同的是对应的每个方法带一个key参数。你可以把这个GenericKeyedObjectPool的对象池看作是一个map的GenericObjectPool,每个key对应一个GenericObjectPool。它用于区别不同类型的对象。比如数据库连接,有可能会连接到不同地址的数据库上面。就可以用这个区分。

4,参数配置类GenericObjectPoolConfig

这个类允许使用者对对象池的一些参数进行调整,根据需要定制对象池。下面说逐一说一下每个参数的含义。

lifo :对象池存储空闲对象是使用的LinkedBlockingDeque,它本质上是一个支持FIFO和FILO的双向的队列,common-pool2中的LinkedBlockingDeque不是Java原生的队列,而有common-pool2重新写的一个双向队列。如果为true,表示使用FIFO获取对象。默认值是true.建议使用默认值。

fairness:common-pool2实现的LinkedBlockingDeque双向阻塞队列使用的是Lock锁。这个参数就是表示在实例化一个LinkedBlockingDeque时,是否使用lock的公平锁。默认值是false,建议使用默认值。

maxWaitMillis:当没有空闲连接时,获取一个对象的最大等待时间。如果这个值小于0,则永不超时,一直等待,直到有空闲对象到来。如果大于0,则等待maxWaitMillis长时间,如果没有空闲对象,将抛出NoSuchElementException异常。默认值是-1;可以根据需要自己调整,单位是毫秒。

minEvictableIdleTimeMillis:对象最小的空闲时间。如果为小于等于0,最Long的最大值,如果大于0,当空闲的时间大于这个值时,执行移除这个对象操作。默认值是1000L * 60L * 30L;即30分钟。这个参数是强制性的,只要空闲时间超过这个值,就会移除。

softMinEvictableIdleTimeMillis:对象最小的空间时间,如果小于等于0,取Long的最大值,如果大于0,当对象的空闲时间超过这个值,并且当前空闲对象的数量大于最小空闲数量(minIdle)时,执行移除操作。这个和上面的minEvictableIdleTimeMillis的区别是,它会保留最小的空闲对象数量。而上面的不会,是强制性移除的。默认值是-1;

numTestsPerEvictionRun:检测空闲对象线程每次检测的空闲对象的数量。默认值是3;如果这个值小于0,则每次检测的空闲对象数量等于当前空闲对象数量除以这个值的绝对值,并对结果向上取整。

testOnCreate:在创建对象时检测对象是否有效,true是,默认值是false。

testOnBorrow:在从对象池获取对象时是否检测对象有效,true是;默认值是false。

testOnReturn:在向对象池中归还对象时是否检测对象有效,true是,默认值是false。

testWhileIdle:在检测空闲对象线程检测到对象不需要移除时,是否检测对象的有效性。true是,默认值是false。

timeBetweenEvictionRunsMillis:空闲对象检测线程的执行周期,即多长时候执行一次空闲对象检测。单位是毫秒数。如果小于等于0,则不执行检测线程。默认值是-1;

blockWhenExhausted:当对象池没有空闲对象时,新的获取对象的请求是否阻塞。true阻塞。默认值是true;

maxTotal:对象池中管理的最多对象个数。默认值是8。

maxIdle:对象池中最大的空闲对象个数。默认值是8。

minIdle:对象池中最小的空闲对象个数。默认值是0。

以上就是common-pool2对象池的配置参数,使用的时候可以根据自己的需要进行调整。

二,common-pool2的应用

使用common-pool2的对象池技术的一个完美例子就是redis的Java客户端JedisPool。大家可以下载Jedis的包,查看源码进行学习。下次我将分享一个我使用common-pool2实现的一个thrift客户端调用的连接池实现。

综上所述,使用common-pool2可以快速的创建一个安全,强大,简单的对象池管理类。它的开源性使它的功能得到了众多项目的检测,是非常安全的。在我们的业务中,如果有需要使用对象池化的操作,可以使用common-pool2快速实现。

三、举个例子

package com.cisdi.dacoo.distributor.pool;import com.cisdi.tagcache.client.CacheClient;
import com.cisdi.tagcache.common.exception.CacheException;
import com.cisdi.tagcache.common.exception.CacheReturnCode;import org.apache.commons.pool2.BaseKeyedPooledObjectFactory;
import org.apache.commons.pool2.PooledObject;
import org.apache.commons.pool2.impl.DefaultPooledObject;
import org.apache.commons.pool2.impl.GenericKeyedObjectPool;
import org.apache.commons.pool2.impl.GenericKeyedObjectPoolConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* 这是一个连接tagcache服务的对象连接池工厂建造类
*/
public class CacheClientPoolFactory {private static final Logger logger =LoggerFactory.getLogger(CacheClientPoolFactory.class);/*** 对象池*/private static GenericKeyedObjectPool<String, CacheClient> pool;/*** 对象池的参数设置*/private static final GenericKeyedObjectPoolConfig config;/*** 对象池每个key最大实例化对象数*/private static final int TOTAL_PERKEY = 10;/*** 对象池每个key最大的闲置对象数*/private static final int IDLE_PERKEY = 3;private static final long MAX_WAIT_MILLS = 3000L;private static final long SOFT_MIN_EVICTABLE_IDLE_MILLS = 3 * 60 * 1000L;static {config = new GenericKeyedObjectPoolConfig();config.setMaxIdlePerKey(IDLE_PERKEY);config.setMaxTotalPerKey(TOTAL_PERKEY);/** 支持jmx管理扩展 */config.setJmxEnabled(true);config.setJmxNamePrefix("CacheClientPool");/** 保证获取有效的池对象 */config.setTestOnBorrow(true);config.setTestOnReturn(false);config.setMaxWaitMillis(MAX_WAIT_MILLS);config.setTestWhileIdle(true);config.setSoftMinEvictableIdleTimeMillis(SOFT_MIN_EVICTABLE_IDLE_MILLS);}/*** 从对象池中获取对象*/public static CacheClient getClient(String key) throws Exception {if (pool == null) {init();}return pool.borrowObject(key);}/*** 归还对象*/public static void returnClient(String key,CacheClient client) {pool.returnObject(key, client);}/*** 归还对象*/public static void returnBrokenClient(String key,CacheClient client) {try {pool.invalidateObject(key, client);} catch (Exception e) {logger.error("Return broken client failed: address={}", key, e);}}/*** 关闭对象池*/public synchronized static void close() {if (pool != null && !pool.isClosed()) {pool.close();pool = null;}}/*** 初始化对象池*/private synchronized static void init() {if (pool == null) {pool = new GenericKeyedObjectPool<String, CacheClient>(new CacheClientPooledFactory(), config);}}private static class CacheClientPooledFactory extends BaseKeyedPooledObjectFactory<String, CacheClient> {@Overridepublic CacheClient create(final String address) throws Exception {String[] values = address.split(":");String host = values[0];int port = Integer.parseInt(values[1]);return new CacheClient(host, port);}@Overridepublic PooledObject<CacheClient> wrap(final CacheClient client) {return new DefaultPooledObject<>(client);}@Overridepublic PooledObject<CacheClient> makeObject(String key) throws Exception {return this.wrap(this.create(key));}@Overridepublic void destroyObject(String key, PooledObject<CacheClient> p) throws Exception {p.getObject().close();}@Overridepublic boolean validateObject(String key, PooledObject<CacheClient> p) {try {p.getObject().exist("test test");} catch (Exception e) {if (e instanceof CacheException&& !((CacheException) e).getCode().equals(CacheReturnCode.CR0505)) {return true;} else {return false;}}return false;}@Overridepublic void activateObject(String key, PooledObject<CacheClient> p) throws Exception {super.activateObject(key, p);}@Overridepublic void passivateObject(String key, PooledObject<CacheClient> p) throws Exception {super.passivateObject(key, p);}}}

参考:https://www.cnblogs.com/wgslucky/p/6105064.html

common-pool2的介绍和使用相关推荐

  1. 面试官问:对象池技术了解吗?apache common pool2呢?

    欢迎关注方志朋的博客,回复"666"获面试宝典 对象池顾名思义就是存放对象的池,与我们常听到的线程池.数据库连接池.http连接池等一样,都是典型的池化设计思想. 对象池的优点就是 ...

  2. common pool2 mysql_连接池Commons Pool2的使用

    客户端这边,如果每次都临时建立一个新的连接,那么连接的开销非常大. 业内常用的连接池组件是 Commons Pool2---版本 2.4.2 packageservice.pool; importor ...

  3. common pool2 mysql_用common-pool自定义资源池

    以前认为连接池,线程池这些东西很高端.因为出了问题之后,总是有人说这个连接池/线程线的配置不对嘛,应该..... 今天学习了下,这个池到底应该怎么设计.发现apache的common-pool早就思考 ...

  4. 自己写的grpc简单连接池,基于common pool2

    17年的时候写的证券的项目,当时交易端是另外一批同事开发的,他们强烈要求用grpc,当时这个东西还不那么成熟,在网上也搜索不到比较完美的第三方的连接池搭配使用,索性就自己写了一个,因为之前thrift ...

  5. AIFF和AIFF-C音频交换文件格式的简单介绍

    正文 AIFF,全称 Audio Interchange File Format,可简写为 Audio IFF 或 AIFF,是苹果公司推出的一种音频文件格式. AIFF-C,是 AIFF 的扩充,C ...

  6. Java 数据持久化系列之池化技术

    点击上方"方志朋",选择"设为星标" 回复"666"获取新整理的面试资料 在上一篇文章Java 数据持久化系列之JDBC中,我们了解到使用 ...

  7. java 状态机_Java 数据持久化系列之池化技术

    在上一篇文章<Java 数据持久化系列之JDBC>中,我们了解到使用 JDBC 创建 Connection 可以执行对应的SQL,但是创建 Connection 会消耗很多资源,所以 Ja ...

  8. 那些优秀的软件架构图,都是怎么画的?

    程序员的成长之路 互联网/程序员/技术/资料共享 关注 阅读本文大概需要 6 分钟. 来自:CodeSheep 当我们想用一张或几张图来描述我们的系统时,是不是经常遇到以下情况: 对着画布无从下手.删 ...

  9. Netty面试题(史上最全)

    文章很长5万字,而且不断更新,建议收藏起来慢慢读!疯狂创客圈总目录 语雀版 | 总目录 码云版| 总目录 博客园版 为您奉上珍贵的学习资源 : 免费赠送 经典图书:<Java高并发核心编程(卷1 ...

  10. 缓存一致性协议硬核讲解

    作者 | 牛在舒 面向护发编程程序员,呵护每一根秀发 从缓存一致性协议说起 相信大家都听说过 "缓存一致性协议",那么它是为了解决哪些问题?在实际应用中的现状又是如何呢? 首先让我 ...

最新文章

  1. 如何快速采集分析平台日志,并进行展示监控?
  2. qt sse指令加速配置
  3. 微信红利末期,新媒体运营除了打造10W+还应该做什么?
  4. 蓝桥杯C++ AB组辅导课 第二讲 二分与前缀和 Acwing
  5. Web Application:Exploded和Web Application:Archive
  6. .net core 使用redis 基于 StackExchange.Redis
  7. C#抽象类与密封类-abstract-sealed
  8. 百度“追杀”66天后,景驰CEO王劲离职,原CTO韩旭接任
  9. 5.产品的三种流程图,你都知道吗?
  10. PLSQL使用for update编辑数据库的坑
  11. maven项目使用mybatis插件Free Mybatis plugin
  12. 简单介绍企业erp系统究竟是什么?
  13. 分类学 · 狡兔为何偏要有三窟???
  14. 【Uly】微软产品开发中的“战争与和平”
  15. zookeeper初步
  16. stm32f407 休眠模式_STM32进入和退出睡眠模式
  17. 用手机计算机计算三次根号,手机自带计算器不行求推荐一个能开3次根号的 – 手机爱问...
  18. 视频文件头解析--mkv
  19. 查看Oracle sys_lob,system表空间满的处理-SYS_LOB0003450292C00039$$
  20. 单片机定时器精准定时_通过51单片机定时器/计数器实现精确延时

热门文章

  1. Oracle11g下导入SDO_GEOMETRY类型的数据异常处理
  2. 深入浅出Oracle Spatial
  3. html怎么把字做成动画效果,用纯CSS实现文字的动态效果
  4. shader实现飞线效果(three.js练习)
  5. Visio绘制网络模型
  6. 如何在低代码开发平台上,实施表单设计流程
  7. UEFI学习(一)-EDK II环境搭建
  8. mac使用的pd虚拟机window黑屏 ---已解决
  9. NetScaler AG自定义用户门户
  10. 干货 | 一起聊聊技术与写作