Apache Commons Pool2 池化技术

news2024/11/30 6:57:42

对象池是一种设计模式,用于管理和重用对象,以提高性能和资源利用率。对象池的概念在许多应用程序中都有广泛应用,特别是在需要频繁创建和销毁对象的情况下,例如数据库连接、线程、HTTP连接等

对象池通过预先创建一组对象并将它们存储在池中,以供需要时获取和使用。当对象不再需要时,它们不会被销毁,而是被返回到池中,以便在后续的请求中重新使用

对象池的优点包括:

  • 减少对象创建和销毁的开销,提高性能和响应时间
  • 最大限度地利用系统资源,避免资源的浪费
  • 控制对象的数量,防止资源耗尽和系统崩溃
  • 提供对象的重用机制,避免频繁的对象创建和垃圾回收

Apache Commons Pool2

Apache Commons Pool2 是一个广泛使用的Java对象池库,被许多开源项目和企业应用程序采用。它提供了通用的对象池化解决方案,可用于管理各种类型的对象,如数据库连接、线程池、HTTP连接等

Commons Pool 工作原理

核心类

ObjectPool

对象池,负责对对象进行生命周期的管理,并提供了对对象池中活跃对象和空闲对象统计的功能

  • 对象的提供与归还:borrowObjectreturnObject
  • 创建对象:addObject
  • 销毁对象:invalidateObject
  • 池中空闲对象数量、被使用对象数量:getNumActivegetNumIdle

PooledObjectFactory

对象工厂类,负责具体对象的创建、初始化,对象状态的销毁和验证

commons-pool2框架本身提供了默认的抽象实现BasePooledObjectFactory,业务方在使用的时候只需要继承该类,然后实现warpcreate方法即可

PooledObject

池化对象,是需要放到ObjectPool对象的一个包装类。添加了一些附加的信息,比如说状态信息,创建时间,激活时间等

池对象状态及流程

PooledObjectState 池对象状态枚举

public enum PooledObjectState {
    //在空闲队列中,还未被使用
    IDLE,
    //使用中
    ALLOCATED,
    //在空闲队列中,当前正在测试是否满足被驱逐的条件
    EVICTION,
	  //不在空闲队列中,目前正在测试是否可能被驱逐。因为在测试过程中,试图借用对象,并将其从队列中删除。
    //回收测试完成后,它应该被返回到队列的头部。
    EVICTION_RETURN_TO_HEAD,
	  //在队列中,正在被校验
    VALIDATION,
	  //不在队列中,当前正在验证。该对象在验证时被借用,由于配置了testOnBorrow,
    //所以将其从队列中删除并预先分配。一旦验证完成,就应该分配它。
    VALIDATION_PREALLOCATED,
	  //不在队列中,当前正在验证。在之前测试是否将该对象从队列中移除时,曾尝试借用该对象。
    //一旦验证完成,它应该被返回到队列的头部。
    VALIDATION_RETURN_TO_HEAD,
	  //无效状态(如驱逐测试或验证),并将/已被销毁
    INVALID,
	  //判定为无效,将会被设置为废弃
    ABANDONED,
	  //正在使用完毕,返回池中
    RETURNING
}

流程理解

此段原文自 https://www.cnblogs.com/haixiang/p/14783955.html

对象存储
 private PooledObject<T> create() throws Exception {
  		.....
 		final PooledObject<T> p;
        try {
            p = factory.makeObject();
        .....
        allObjects.put(new IdentityWrapper<>(p.getObject()), p);
        return p;
 }

我们查看allObjects,所有对象都存储于ConcurrentHashMap,除了被杀掉的对象

/*
 * All of the objects currently associated with this pool in any state. It
 * excludes objects that have been destroyed. The size of
 * {@link #allObjects} will always be less than or equal to {@link
 * #_maxActive}. Map keys are pooled objects, values are the PooledObject
 * wrappers used internally by the pool.
 */
private final Map<IdentityWrapper<T>, PooledObject<T>> allObjects =
    new ConcurrentHashMap<>();
对象取用逻辑
  • 首先根据AbandonedConfig配置判断是否取用对象前执行清理操作
  • 再从idleObject中尝试获取对象,获取不到就创建新的对象
    • 判断blockWhenExhausted是否设置为true,是的话按照设置的borrowMaxWaitMillis属性等待可用对象(这个配置的意思是当对象池的active状态的对象数量已经达到最大值maxinum时是否进行阻塞直到有空闲对象)
  • 有可用对象后调用工厂的factory.activateObject方法激活对象
  • getTestOnBorrow设置为true时,调用factory.validateObject(p)对对象进行校验,通过校验后执行下一步
  • 调用updateStatsBorrow方法,在对象被成功借出后更新一些统计项,例如返回对象池的对象个数等

//....
private final LinkedBlockingDeque<PooledObject<T>> idleObjects;
//....
public T borrowObject(final long borrowMaxWaitMillis) throws Exception {
        assertOpen();
        final AbandonedConfig ac = this.abandonedConfig;
        if (ac != null && ac.getRemoveAbandonedOnBorrow() &&
                (getNumIdle() < 2) &&
                (getNumActive() > getMaxTotal() - 3) ) {
            removeAbandoned(ac);
        }
        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;
            p = idleObjects.pollFirst();
            if (p == null) {
                p = create();
                if (p != null) {
                    create = true;
                }
            }
            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");
                }
            }
            if (!p.allocate()) {
                p = null;
            }
            if (p != null) {
                try {
                    factory.activateObject(p);
                } catch (final Exception e) {
                    try {
                        destroy(p, DestroyMode.NORMAL);
                    } 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;
                    }
                }
                if (p != null && getTestOnBorrow()) {
                    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, DestroyMode.NORMAL);
                            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();
    }
对象的激活和钝化

在对象使用完被返回对象池时,如果校验失败直接销毁,如果校验通过需要先钝化对象passivateObject(PooledObject<T> p)再存入空闲队列。至于激活对象的方法activateObject(PooledObject<T> p)在上述取用对象时也会先激活再被取出。因此我们可以发现处于空闲和使用中的对象他们除了状态不一致,我们也可以通过激活和钝化的方式在他们之间增加新的差异

例如我们要做一个Elasticsearch连接池,每个对象就是一个带有ip和端口的连接实例,很显然访问es集群是多个不同的ip,所以每次访问的ip不一定相同,我们则可以在激活操作为对象赋值ip和端口,钝化操作中将ip和端口归为默认值或者空,这样流程更为标准

对象回收机制

EvictionTimer为回收对象的定时器,当且仅当设置了timeBetweenEvictionRunsMillis参数后才会开启定时清理任务

对象状态为evict,直接调用destroy进行回收。否则会进行进一步地处理,逻辑如下:

  1. 尝试激活对象,如果激活失败则认为对象已经不再存活,直接调用destroy进行销毁
  2. 在激活对象成功的情况下,会通过validateObject方法取校验对象状态,如果校验失败,则说明对象不可用,需要进行销毁
// org.apache.commons.pool2.impl.GenericObjectPool#evict
if (evict) {
    destroy(underTest);
    destroyedByEvictorCount.incrementAndGet();
} else {
    if (testWhileIdle) {
        boolean active = false;
        try {
            factory.activateObject(underTest);
            active = true;
        } catch (Exception e) {
            destroy(underTest);
            destroyedByEvictorCount.incrementAndGet();
        }
        if (active) {
            if (!factory.validateObject(underTest)) {
                destroy(underTest);
                destroyedByEvictorCount.incrementAndGet();
            } else {
                try {
                    factory.passivateObject(underTest);
                } catch (Exception e) {
                    destroy(underTest);
                    destroyedByEvictorCount.incrementAndGet();
                }
            }
        }
    }
    if (!underTest.endEvictionTest(idleObjects)) {
        // TODO - May need to add code here once additional
        // states are used
    }
}

常用参数详解

参数类型(默认值)描述
lifoboolean(true)当去获取对象池中的空闲实例时,是否需要遵循后进先出的原则
fairnessboolean(false)当对象池处于exhausted状态,即可用实例为空时,大量线程在同时阻塞等待获取可用的实例,fairness配置来控制是否启用公平锁算法,即先到先得。这一项的前提是blockWhenExhausted配置为true
maxTotalint(8)对象池中最大使用数量
maxIdleint(8)对象中空闲对象最大数量
minIdleint(0)对象池中空闲对象最小数量
maxWaitMillislong(-1)最大阻塞时间,当对象池处于exhausted状态,即可用实例为空时,大量线程在同时阻塞等待获取可用的实例,如果阻塞时间超过了maxWaitMillis将会抛出异常
testOnCreateboolean(false)创建对象前,是否校验该新对象的有效性
testOnBorrowboolean(false)取用对象前,是否检验对象的有效性
testOnReturnboolean(false)归还对象前,是否检验对象的有效性
testWhileIdleboolean(false)当回收器在扫描空闲对象时,是否校验对象的有效性
numTestsPerEvictionRunint(3)根据该值x可以推导出一个数值n,标识回收过程需要检查多少个空闲对象。如果x>=0,那么n=x。如果x<0,那么n=(空闲对象数量/x的绝对值)向上取整,假设空闲对象一共有10个,该值配置为-3,那么就意味着这次回收需要检查4个空闲对象
timeBetweenEvictionRunsMillislong(-1)回收器线程多久执行一次空闲对象回收
softMinEvictableIdleTimeMillislong(-1)软回收时间阈值一个对象如果空闲时间超过了该值(毫秒),并且空闲对象的数量已经大于了minIdle时,就可以被回收器回收
minEvictableIdleTimeMillislong(1000L * 60L * 30L)硬回收时间阈值一个对象如果空闲时间超过了该值,即使空闲对象的数量已经小于minIdle了,一样也会被回收器回收

假如在某个高负载的系统里,对象频繁被借出、被归还。此时推荐对象池配置为

  1. testOnBorrowtestOnReturn都设置为false,提升性能
  2. timeBetweenEvictionRunsMillis设置为60000,一分钟进行一次空闲对象的回收检查
  3. numTestsPerEvictionRun设置为-1,检查所有空闲对象
  4. minEvictableIdleTimeMillis设置为180000,空闲超过3分钟的可以被回收
  5. testWhileIdle设置为true,不管空闲时间是否超时,每个空闲对象都检查下有效性,无效的一样被回收

通过异步的回收器来尽可能的保证空闲对象的有效性,减少同步调用时有效性检查导致的响应延迟、以及有效性检查对底层带来的访问压力

总结

Apache Commons Pool2是一个用于对象池化的Java库,它提供了一种管理和重用对象实例的机制,旨在改善应用程序的性能和资源利用率。下面是对Apache Commons Pool2的总结:

  • 对象池化:Apache Commons Pool2允许您创建和管理一个对象池,该对象池中保存着可重用的对象实例。通过将对象保存在池中而不是频繁地创建和销毁对象,可以减少系统开销,提高性能。
  • 对象生命周期管理:Pool2提供了生命周期管理功能,允许您定义在对象从池中借出和归还时的初始化和清理操作。这确保了从池中借出的对象始终处于一种可用状态。
  • 池化策略:Pool2支持多种池化策略,如通用对象池、软引用对象池和弱引用对象池。您可以根据应用程序的需求选择适合的策略。
  • 线程安全:Pool2是线程安全的,它提供了同步机制来处理多个线程同时访问池的情况,确保对象的正确分配和归还。
  • 配置灵活:Pool2允许您通过配置文件或编程方式来配置对象池的各种属性,如最大池大小、最小空闲对象数、池中对象的最大空闲时间等。这使得您可以根据应用程序的需求进行优化和调整。
  • 连接池和资源池:Pool2不仅适用于对象池,还可用于连接池和其他资源的池化管理。这使得它在处理数据库连接、线程池等资源时非常有用。

总的来说,Apache Commons Pool2是一个功能强大且灵活的对象池化库,可帮助您管理和重用对象实例,提高应用程序的性能和资源利用率。无论是在Web应用程序、并发编程还是资源管理方面,Pool2都是一个可靠的选择


参考资料:

  1. Apache Commons Pool
  2. commons-pool2 池化技术探究
  3. 对象池
  4. apache common pool2原理与实战

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1054939.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

MPLS虚拟专用网--跨域OptionC方案

OptionC方案 前面介绍的两种方式都能够满足跨域VPN的组网需求,但这两种方式也都需要ASBR参与VPN-IPv4路由的维护和发布。当每个AS都有大量的VPN路由需要交换时,ASBR就很可能阻碍网络进一步的扩展。 解决上述问题的方案是:ASBR不维护或发布VPN-IPv4路由,PE之间直接交换VPN-…

【算法】算法基础课模板大全

一、基础算法 快速排序算法模板 void quick_sort(int q[], int l, int r) {//递归的终止情况if (l > r) return;//选取分界线。这里选数组中间那个数int i l - 1, j r 1, x q[l r >> 1];//划分成左右两个部分while (i < j){do i ; while (q[i] < x);do …

用两个栈来实现队列

typedef int SltDatatype; typedef struct Stack {SltDatatype* a;//开辟栈的动态内存空间int top;//记录栈顶int capacity;//记录容量 }ST;void STInit(ST* ps);//栈的初始化 void STDestroy(ST* ps);//释放 void STPush(ST* ps, SltDatatype x);//入栈 void STPop(ST* ps);//…

喝健康白酒 有益生心健康

中国的制酒史源远流长&#xff0c;酒渗透在中华五千年的文化中。酒与烟不同&#xff0c;烟对人体有百害而无一利&#xff0c;而对于酒&#xff0c;若掌握好饮酒的度&#xff0c;对人体有一定的养生作用&#xff0c;所以我们通常会说“戒烟限酒”。 据一些专家研究&#xff0c;…

下载盗版网站视频并将.ts视频文件合并

. 1.分析视频请求123 2.数据获取和拼接 1.分析视频请求 1 通过抓包观察我们发现视频是由.ts文件拼接成的每一个.ts文件代表一小段2 通过观察0.ts和1.ts的url我们发现他们只有最后一段不同我们网上找到url获取的包3 我们发现index.m3u8中储存着所有的.ts文件名在拼接上前面固定…

重置Jetson设备的Ubuntu密码:通过挂载根目录到另一个Linux系统

在本文中&#xff0c;我们将介绍如何在忘记Ubuntu 20.04密码的情况下重置密码。我们将通过将Ubuntu的根目录挂载到另一个Linux系统来实现这一目的。我们还将介绍chroot命令的功能。 1. 背景 最近&#xff0c;我们研发团队遇到了一个棘手的问题。一台用于研发&#xff0c;多人使…

验证曲线(validation_curve)项目实战

验证曲线 validation_curve 一、简介 validation_curve验证曲线&#xff0c;可确定不同参数值下的训练和测试分数 根据指定参数的不同值计算估计器的得分 这与使用一个参数的网格搜索类似。不过&#xff0c;这也会计算训练得分&#xff0c;只是一个用于绘制结果的工具。 二、…

十个有用的 Vue.js 自定义 Hook

Vue.js 是我使用的第一个 JavaScript 框架。 我可以说 Vue.js 是我进入 JavaScript 世界的第一扇门之一。 目前&#xff0c;Vue.js 仍然是一个很棒的框架。 我认为有了组合 API&#xff0c;Vue.js 只会增长得更多。 在本文中&#xff0c;我将向分享 10 个可以使用 Vue.js 制作…

计算机竞赛 深度学习手势识别 - yolo python opencv cnn 机器视觉

文章目录 0 前言1 课题背景2 卷积神经网络2.1卷积层2.2 池化层2.3 激活函数2.4 全连接层2.5 使用tensorflow中keras模块实现卷积神经网络 3 YOLOV53.1 网络架构图3.2 输入端3.3 基准网络3.4 Neck网络3.5 Head输出层 4 数据集准备4.1 数据标注简介4.2 数据保存 5 模型训练5.1 修…

竞赛 多目标跟踪算法 实时检测 - opencv 深度学习 机器视觉

文章目录 0 前言2 先上成果3 多目标跟踪的两种方法3.1 方法13.2 方法2 4 Tracking By Detecting的跟踪过程4.1 存在的问题4.2 基于轨迹预测的跟踪方式 5 训练代码6 最后 0 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 &#x1f6a9; 深度学习多目标跟踪 …

【笔试强训day01】组队竞赛 删除公共字符

​&#x1f47b;内容专栏&#xff1a; 笔试强训集锦 &#x1f428;本文概括&#xff1a;C笔试面试常考题之笔试强训day01。 &#x1f43c;本文作者&#xff1a; 阿四啊 &#x1f438;发布时间&#xff1a;2023.10.1 一、day01 1.组队竞赛 题目描述 题目描述&#xff1a;牛牛举…

【JavaEE】JavaScript

JavaScript 文章目录 JavaScript组成书写方式行内式内嵌式外部式&#xff08;推荐写法&#xff09; 输入输出变量创建动态类型基本数据类型数字类型特殊数字值 String转义字符求长度字符串拼接布尔类型undefined未定义数据类型null 运算符条件语句if语句三元表达式switch 循环语…

【算法|贪心算法系列No.3】leetcode334. 递增的三元子序列

个人主页&#xff1a;兜里有颗棉花糖 欢迎 点赞&#x1f44d; 收藏✨ 留言✉ 加关注&#x1f493;本文由 兜里有颗棉花糖 原创 收录于专栏【手撕算法系列专栏】【LeetCode】 &#x1f354;本专栏旨在提高自己算法能力的同时&#xff0c;记录一下自己的学习过程&#xff0c;希望…

[C++_containers]10分钟让你掌握vector

前言 在一个容器的创建或是使用之前&#xff0c;我们应该先明白这个容器的一些特征。 我们可以通过文档来来了解&#xff0c;当然我也会将重要的部分写在下面。 1. vector 是表示可变大小数组的序列容器。 2. 就像数组一样&#xff0c; vector 也采用的连续存储空间来存储元…

picoctf_2018_shellcode

picoctf_2018_shellcode Arch: i386-32-little RELRO: Partial RELRO Stack: No canary found NX: NX disabled PIE: No PIE (0x8048000) RWX: Has RWX segments32位&#xff0c;啥都没开 这个看着挺大的&#xff0c;直接来个ROPchain&#xff0c;…

Mapfree智驾方案,怎样实现成本可控?

整理|睿思 编辑|祥威 编者注&#xff1a;本文是HiEV出品的系列直播「智驾地图之变」第二期问答环节内容整理。 元戎启行副总裁刘轩与连线嘉宾奥维咨询董事合伙人张君毅、北汽研究总院智能网联中心专业总师林大洋、主持嘉宾周琳展开深度交流&#xff0c;并进行了答疑。 本期元…

新手--安装好Quartus II13.0(带modelsim集成包)并用Quartus II搭建一个工程

前言 今天是国庆节&#xff0c;我们正式来学习Quartus II13.0软件的安装与使用。学习verilog与学习C语言都是学习一门语言&#xff0c;那么学习一门语言&#xff0c;光看理论不敲代码绝对是学习不好的。要用verilog语言敲代码&#xff0c;就要像C语言那样搭建起语言的编译环境&…

USART串口协议

通信接口 •通信的目的&#xff1a;将一个设备的数据传送到另一个设备&#xff0c;扩展硬件系统 • 通信协议&#xff1a;制定通信的规则&#xff0c;通信双方按照协议规则进行数据收发 全双工&#xff1a;指通信双方能够同时进行双向通信&#xff0c;一般来说&#xff0c;全双…

扩容领跑者 Arbitrum 抢占 Layer3 竞争高地

近段时间以来&#xff0c;Arbitrum 凭借创新技术和优越生态系统逐渐成为顶尖的以太坊扩容解决方案。当新一轮 Layer3 竞争在 Rollup 领域展开时&#xff0c;Arbitrum 和 Optimism 始终是备受瞩目的两大角色。Optimism 以独特的 OP Stack 进行水平扩展&#xff0c;而 Arbitrum 则…

K-Means(下):数据分析 | 数据挖掘 | 十大算法之一

⭐️⭐️⭐️⭐️⭐️欢迎来到我的博客⭐️⭐️⭐️⭐️⭐️ &#x1f434;作者&#xff1a;秋无之地 &#x1f434;简介&#xff1a;CSDN爬虫、后端、大数据领域创作者。目前从事python爬虫、后端和大数据等相关工作&#xff0c;主要擅长领域有&#xff1a;爬虫、后端、大数据…