对象池技术其实蛮常见的,比如线程池、数据库连接池

他们的特点是:对象创建代价较高、比较消耗资源、比较耗时;

比如 mysql数据库连接建立就要先建立 tcp三次握手、发送用户名/密码、进行身份校验、权限校验等很多步骤才算是 db连接建立成功;要是每次使用的时候才去创建会比较影响性能,而且也不能无限制的创建太多

所以,这种对象使用完后不立即释放资源,一般是先放到一个池子里暂存起来,下次就能直接从池子里拿出现成可用的对象

1 对象池需要具备的能力

所以,为了让这类资源对象的使用方能够复用资源、快速获取可用对象,这个池子得具备的能力有哪些?

  1. 首先有个容器的数据结构,能存放多个对象,也有数量上限
  2. 维持一定数量的常驻对象,这个数量如果和 qps * rt 匹配的话,业务处理就都能直接获取可用对象,不需要消耗对象创建的时间了
  3. 能应对突发流量
  4. 超时获取,一定时间没有获取成功就抛出异常,不卡死业务线程
  5. 具有活性检测机制, 从容器拿出来的对象得是可用的

1 核心流程

1.1对象获取流程

1.2 活性检测

2 实现

为了实现前面提到的容器具备的能力,以及对象获取流程,需要考虑几个东西:

  1. 容器的数据结构选择

    用 List、 Map 还是 Queue ?亦或是组合起来用?

  2. 空闲对象要不要单独用要给集合存一份?方便判断是否空、阻塞等待?

    比如将空闲对象,用一个blockingqueue存一下,就能利用阻塞队列的能力实现超时等待

  3. 检测机制

    • 在什么时候检测:常见的有 testOnBorrow 在申请到的时候检测、testOnReturn在归还的时候检测 这两个对性能有些影响; 单独开个检查线程,定时去扫描检查,这个是异步的 不会有testOnBorrow和testOnReturn的性能影响
    • 检测哪些对象: 比如空闲超过 500ms 的对象
    • 如何检查:这个需要根据具体对象的类型来,比如db连接的话一般是发送 “select 1” 看是否能正常执行

3 一个通用实现 apache commons pool

通过前面的介绍,可以知道对象池技术的核心过程大同小异,可以将对象获取流程、活性检测机制等封装成一个通用的工具,将对象本身的创建、活性检测逻辑开放给具体的对象实现来完成; apache commons pool 就是这么个工具, jedis底层的连接池就是直接用的这个

3.1 核心数据结构

  • LinkedBlockingDeque<PooledObject<T>> idleObjects 空闲对象双向阻塞队列
  • Map<IdentityWrapper<T>, PooledObject<T>> allObjects = new ConcurrentHashMap<>(); 所有对象的map

apache commons pool 的容器用的 ConcurrentHashMap,并且将空闲的对象用一个双向阻塞队列单独连接起来;

这样他就能利用这个阻塞队列本身的特性,达到阻塞获取的逻辑,如果 idleObjects 是空的,就能 take()/poll(timeout) 阻塞在这里,等待其他线程归还对象队列里

3.2 核心对象定义

  • PooledObject 可池化的对象:包含真实对象以及创建时间、取出时间、空闲时间等指标信息
  • PooledObjectFactory 对象工厂,负责对象的创建、销毁、状态扭转、检查等逻辑;它有个默认实现

    DefaultPooledObject 提供了基本的实现,一般只要继承它重写对象创建和验活逻辑就可以了
  • GenericObjectPool 就是对象容器了

3.3 代码细节

从池子中获取对象

T borrowObject(final long borrowMaxWaitMillis) {

    //省略一些代码 ...
PooledObject<T> p = null; // Get local copy of current config so it is consistent for entire
// method execution
final boolean blockWhenExhausted = getBlockWhenExhausted(); boolean create;
final long waitTime = System.currentTimeMillis(); while (p == null) {
create = false;
// 空闲队列 队首如果是空的,则创建一个新的对象
// 创建的逻辑里会校验是否超过最大连接数,然后利用 PooledObjectFactory创建对象
p = idleObjects.pollFirst();
if (p == null) {
p = create();
if (p != null) {
create = true;
}
} // 阻塞从 idleObject 空闲阻塞队列获取对象
if (blockWhenExhausted) {
if (p == null) {
if (borrowMaxWaitMillis < 0) {
p = idleObjects.takeFirst();
} else {
//超时等待
p = idleObjects.pollFirst(borrowMaxWaitMillis,
TimeUnit.MILLISECONDS);
}
}
if (p == null) {
throw new NoSuchElementException(
"Timeout waiting for idle object");
}
} else {
if (p == null) {
throw new NoSuchElementException("Pool exhausted");
}
} // 状态转换为已分配 ALLOCATE,记录借出时间等信息
if (!p.allocate()) {
p = null;
} if (p != null) {
try {
// 允许 PooledObjectFactory 在成功获取到对象后做一些事,
// 比如jedis连接池获取到连接后会执行 select db 切换db
factory.activateObject(p);
} catch (final Exception e) {
try {
destroy(p);
} catch (final Exception e1) {
// Ignore - activation failure is more important
}
p = null;
if (create) {
final NoSuchElementException nsee = new NoSuchElementException(
"Unable to activate object");
nsee.initCause(e);
throw nsee;
}
}
// 如果 testOnBorrow=true, 或者 testOnCreate=true + 此次对象是新建的
// 则会去校验对象的有效性 PooledObjectFactory#validateObject()
if (p != null && (getTestOnBorrow() || create && getTestOnCreate())) {
boolean validate = false;
Throwable validationThrowable = null;
try {
validate = factory.validateObject(p);
} catch (final Throwable t) {
PoolUtils.checkRethrow(t);
validationThrowable = t;
}
// 如果对象有效性校验失败,则销毁掉
if (!validate) {
try {
destroy(p);
destroyedByBorrowValidationCount.incrementAndGet();
} catch (final Exception e) {
// Ignore - validation failure is more important
}
p = null;
if (create) {
final NoSuchElementException nsee = new NoSuchElementException(
"Unable to validate object");
nsee.initCause(validationThrowable);
throw nsee;
}
}
}
}
} updateStatsBorrow(p, System.currentTimeMillis() - waitTime); return p.getObject();
}

归还对象

public void returnObject(final T obj) {
// 校验下对象是否还存在
final PooledObject<T> p = allObjects.get(new IdentityWrapper<>(obj)); if (p == null) {
if (!isAbandonedConfig()) {
throw new IllegalStateException(
"Returned object not currently part of this pool");
}
return; // Object was abandoned and removed
} // 状态标记为 “归还中”
synchronized(p) {
final PooledObjectState state = p.getState();
if (state != PooledObjectState.ALLOCATED) {
throw new IllegalStateException(
"Object has already been returned to this pool or is invalid");
}
p.markReturning(); // Keep from being marked abandoned
} final long activeTime = p.getActiveTimeMillis(); // 如果 testOnReturn=true,则在归回时校验对象是否还有效,如果无效了就销毁掉
if (getTestOnReturn()) {
if (!factory.validateObject(p)) {
try {
destroy(p);
} catch (final Exception e) {
swallowException(e);
}
try {
ensureIdle(1, false);
} catch (final Exception e) {
swallowException(e);
}
updateStatsReturn(activeTime);
return;
}
} try {
factory.passivateObject(p);
} catch (final Exception e1) {
swallowException(e1);
try {
destroy(p);
} catch (final Exception e) {
swallowException(e);
}
try {
ensureIdle(1, false);
} catch (final Exception e) {
swallowException(e);
}
updateStatsReturn(activeTime);
return;
} if (!p.deallocate()) {
throw new IllegalStateException(
"Object has already been returned to this pool or is invalid");
} // 如果此时对象池已经关闭了, 或者当前空闲对象数量大于maxIdle(最大空闲数量)则直接销毁掉
final int maxIdleSave = getMaxIdle();
if (isClosed() || maxIdleSave > -1 && maxIdleSave <= idleObjects.size()) {
try {
destroy(p);
} catch (final Exception e) {
swallowException(e);
}
} else {
if (getLifo()) {
idleObjects.addFirst(p);
} else {
idleObjects.addLast(p);
}
if (isClosed()) {
// Pool closed while object was being added to idle objects.
// Make sure the returned object is destroyed rather than left
// in the idle object pool (which would effectively be a leak)
clear();
}
}
updateStatsReturn(activeTime);
}

开启定期检查任务

final void startEvictor(final long delay) {
synchronized (evictionLock) {
// 关闭前已有的清理任务
if (null != evictor) {
EvictionTimer.cancel(evictor, evictorShutdownTimeoutMillis, TimeUnit.MILLISECONDS);
evictor = null;
evictionIterator = null;
} // 间隔时间大于0的话(默认为-1),才创建定时清理任务Evictor
// Evictor 是一个 Runable任务, 它会检查空闲队列里的对象数量是否超过 maxIdle,空闲时长是否超过 minEvictableTimeMillis
if (delay > 0) {
evictor = new Evictor();
EvictionTimer.schedule(evictor, delay, delay);
}
}
}

对象池技术和通用实现GenericObjectPool的更多相关文章

  1. Java对象池技术的原理及其实现

    看到一片有关于java 对象基础知识,故转载一下,同时学习一下. 摘 要 本文在分析对象池技术基本原理的基础上,给出了对象池技术的两种实现方式.还指出了使用对象池技术时所应注意的问题. 关键词 对象池 ...

  2. JedisCluster中应用的Apache Commons Pool对象池技术

    对象池技术在服务器开发上应用广泛.在各种对象池的实现中,尤其以数据库的连接池最为明显,可以说是每个服务器必须实现的部分.   apache common pool 官方文档可以参考:https://c ...

  3. Java中的对象池技术

    java中的对象池技术,是为了方便快捷地创建某些对象而出现的,当需要一个对象时,就可以从池中取一个出来(如果池中没有则创建一个),则在需要重复重复创建相等变量时节省了很多时间.对象池其实也就是一个内存 ...

  4. 基于Netty包中的Recycler实现的对象池技术详解

    一.业务背景 当项目中涉及到频繁的对象的创建和回收的时候,就会出现频繁GC的情况,这时就出现了池化的技术来实现对象的循环使用从而避免对象的频繁回收,Netty包下的Recycler就实现了这一功能.当 ...

  5. 屏幕坐标和世界坐标的转换+对象池技术(3D打地鼠小游戏)

    游戏中可能经常会遇到需要某个物体跟着鼠标移动,然后又需要把物体放在某个鼠标指定的位置 实现方式 Camera.main.WorldToScreenPoint Camera.main.ScreenToW ...

  6. 大数据技术之_27_电商平台数据分析项目_02_预备知识 + Scala + Spark Core + Spark SQL + Spark Streaming + Java 对象池

    第0章 预备知识0.1 Scala0.1.1 Scala 操作符0.1.2 拉链操作0.2 Spark Core0.2.1 Spark RDD 持久化0.2.2 Spark 共享变量0.3 Spark ...

  7. Apache Common-pool2对象池分析和应用

    Apache Common-pool2包提供了一个通用的对象池技术的实现.可以很方便的基于它来实现自己的对象池,比如DBCP和Jedis他们的内部对象池的实现就是依赖于Common-pool2. 对象 ...

  8. Java中对象池的本质是什么?(实战分析版)

    简介 对象池顾名思义就是存放对象的池,与我们常听到的线程池.数据库连接池.http连接池等一样,都是典型的池化设计思想. 对象池的优点就是可以集中管理池中对象,减少频繁创建和销毁长期使用的对象,从而提 ...

  9. common-pool2对象池(连接池)的介绍及使用

    我们在服务器开发的过程中,往往会有一些对象,它的创建和初始化需要的时间比较长,比如数据库连接,网络IO,大数据对象等.在大量使用这些对象时,如果不采用一些技术优化,就会造成一些不可忽略的性能影响.一种 ...

随机推荐

  1. 039.Python使用TCP实现多用户并发

    使用TCP实现多用户并发 在前面的实验中,TCP建立连接时,只能允许一个用户连接,当第二个用户建立连接时,发送的信息,服务端是没有办法接受,只有当第一个用户退出时,才能接受到第二个用户的请求,并实现通 ...

  2. HC-05底层驱动

    INT8U BT_INIT(const char * pNAME, INT32U BAUD, INT8U ROLE, INT32U PSWD) { INT8U OS_ERR = OS_ERR_NONE ...

  3. spring mvc下实现通过邮箱找回密码功能

    1功能分析 通过spring mvc框架实现通过邮箱找回密码. 2 实现分析 主要是借助某个邮箱的pop3/smtp服务实现的邮件代发功能. 3 源码分析 3.1首先在用户表对应的javabean中加 ...

  4. python程序打包成exe(使用pyinstaller)

    pyinstaller下载地址:https://github.com/pyinstaller/pyinstaller/ (这个文件能够自动安装依赖项,其他版本的貌似还要自己安装依赖项) 下载之后解压到 ...

  5. 一些固化了的语音识别模块demo, 手机重力传感器获取

    helloH5 这个软件里面有好多这个东东哦

  6. docker仓库登录 配置insecure-registries

    1. 配置/etc/docker/daemon.json # cat /etc/docker/daemon.json { "registry-mirrors": ["ht ...

  7. 在微信框架模块中,基于Vue&Element前端的微信公众号和企业微信的用户绑定

    在一个和微信相关的业务管理系统,我们有时候需要和用户的微信账号信息进行绑定,如对公众号.企业微信等账号绑定特定的系统用户,可以进行扫码登录.微信信息发送等操作,用户的绑定主要就是记录公众号用户的ope ...

  8. 【MybatisPlus】数据库的datetime类型字段为空的时候,报错空指针?

    一.发现经历 事情是这样的,我今天本来要演示系统,就去前端同学的页面上点一点.不小心点到了其他同事编写的服务,然后界面就报错了.这给我吓得,这还能演示吗这.然后,我就去服务器查看了一下日志,发现了如下 ...

  9. YOLOV4各个创新功能模块技术分析(一)

    YOLOV4各个创新功能模块技术分析(一) 简 介 yolov4论文:YOLOv4: Optimal Speed and Accuracy of Object Detection arxiv:http ...

  10. 常用深度学习框——Caffe/ TensorFlow / Keras/ PyTorch/MXNet

    常用深度学习框--Caffe/ TensorFlow / Keras/ PyTorch/MXNet 一.概述 近几年来,深度学习的研究和应用的热潮持续高涨,各种开源深度学习框架层出不穷,包括Tenso ...