对象池 GenericObjectPool 配置参数详解

举报
Huazie 发表于 2024/07/23 14:23:08 2024/07/23
【摘要】 本篇 Huazie 向大家介绍 对象池 GenericObjectPool 的配置参数【即GenericObjectPoolConfig中的配置属性】

引言

使用 GenericObjectPool 之前,我们有必要了解一下 GenericObjectPoolConfig,下面将详细说明一下其相关的配置参数。

1. 对象池涉及依赖

    <dependency>
        <groupId>org.apache.commons</groupId>
        <artifactId>commons-pool2</artifactId>
        <version>2.4.3</version>
    </dependency>

2. 父类BaseObjectPoolConfig配置参数

BaseObjectPoolConfig 提供了子类共享的通用属性的实现。将使用公共常量定义的默认值创建此类的新实例。 特别注意,该类不是线程安全的。

public abstract class BaseObjectPoolConfig extends BaseObject implements Cloneable {
    private boolean lifo = DEFAULT_LIFO;
    
    private boolean fairness = DEFAULT_FAIRNESS;
    
    private long maxWaitMillis = DEFAULT_MAX_WAIT_MILLIS;
    
    private long minEvictableIdleTimeMillis = DEFAULT_MIN_EVICTABLE_IDLE_TIME_MILLIS;
    
    private long evictorShutdownTimeoutMillis = DEFAULT_EVICTOR_SHUTDOWN_TIMEOUT_MILLIS;
    
    private long softMinEvictableIdleTimeMillis = DEFAULT_SOFT_MIN_EVICTABLE_IDLE_TIME_MILLIS;
    
    private int numTestsPerEvictionRun = DEFAULT_NUM_TESTS_PER_EVICTION_RUN;
    
    private String evictionPolicyClassName = DEFAULT_EVICTION_POLICY_CLASS_NAME;
    
    private boolean testOnCreate = DEFAULT_TEST_ON_CREATE;
    
    private boolean testOnBorrow = DEFAULT_TEST_ON_BORROW;
    
    private boolean testOnReturn = DEFAULT_TEST_ON_RETURN;
    
    private boolean testWhileIdle = DEFAULT_TEST_WHILE_IDLE;
    
    private long timeBetweenEvictionRunsMillis = DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS;

    private boolean blockWhenExhausted = DEFAULT_BLOCK_WHEN_EXHAUSTED;

    private boolean jmxEnabled = DEFAULT_JMX_ENABLE;

    private String jmxNamePrefix = DEFAULT_JMX_NAME_PREFIX;

    private String jmxNameBase = DEFAULT_JMX_NAME_BASE;
}

2.1 lifo

提供了后进先出(LIFO)与先进先出(FIFO)两种行为模式的池;
默认 DEFAULT_LIFO = true, 当池中有空闲可用的对象时,调用borrowObject 方法会返回最近(后进)的实例。

org.apache.commons.pool2.impl.GenericObjectPool

if (getLifo()) {
    idleObjects.addFirst(p);
} else {
    idleObjects.addLast(p);
}

2.2 fairness

当从池中获取资源或者将资源还回池中时,是否使用java.util.concurrent.locks.ReentrantLock.ReentrantLock 的公平锁机制。
默认 DEFAULT_FAIRNESS = false

org.apache.commons.pool2.impl.GenericObjectPool

    idleObjects = new LinkedBlockingDeque<PooledObject<T>>(config.getFairness());

2.3 maxWaitMillis

当连接池资源用尽后,调用者获取连接时的最大等待时间(单位 :毫秒);
默认值 DEFAULT_MAX_WAIT_MILLIS = -1L, 永不超时。

org.apache.commons.pool2.impl.GenericObjectPool

    @Override
    public T borrowObject() throws Exception {
        return borrowObject(getMaxWaitMillis());
    }

2.4 minEvictableIdleTimeMillis

连接的最小空闲时间,达到此值后该空闲连接可能会被移除(还需看是否已达最大空闲连接数);
默认值 DEFAULT_MIN_EVICTABLE_IDLE_TIME_MILLIS = 1000L * 60L * 30L

org.apache.commons.pool2.impl.GenericObjectPool

    final EvictionConfig evictionConfig = new EvictionConfig(
                        getMinEvictableIdleTimeMillis(),
                        getSoftMinEvictableIdleTimeMillis(),
                        getMinIdle());

2.5 evictorShutdownTimeoutMillis

关闭驱逐线程的超时时间;
默认值 DEFAULT_EVICTOR_SHUTDOWN_TIMEOUT_MILLIS = 10L * 1000L

org.apache.commons.pool2.impl.BaseGenericObjectPool

    final void startEvictor(final long delay) {
        synchronized (evictionLock) {
            if (null != evictor) {
                EvictionTimer.cancel(evictor, evictorShutdownTimeoutMillis, TimeUnit.MILLISECONDS);
                evictor = null;
                evictionIterator = null;
            }
            if (delay > 0) {
                evictor = new Evictor();
                EvictionTimer.schedule(evictor, delay, delay);
            }
        }
    }

2.6 softMinEvictableIdleTimeMillis

连接空闲的最小时间,达到此值后空闲链接将会被移除,且保留 minIdle 个空闲连接数;
默认值 DEFAULT_SOFT_MIN_EVICTABLE_IDLE_TIME_MILLIS = -1

2.7 numTestsPerEvictionRun

检测空闲对象线程每次运行时检测的空闲对象的数量;

  • 如果 numTestsPerEvictionRun>=0, 则取 numTestsPerEvictionRun 和池内的连接数 的较小值 作为每次检测的连接数;
  • 如果 numTestsPerEvictionRun<0,则每次检查的连接数是检查时池内连接的总数除以这个值的绝对值再向上取整的结果;

默认值 DEFAULT_NUM_TESTS_PER_EVICTION_RUN = 3

org.apache.commons.pool2.impl.GenericObjectPool

    private int getNumTests() {
        final int numTestsPerEvictionRun = getNumTestsPerEvictionRun();
        if (numTestsPerEvictionRun >= 0) {
            return Math.min(numTestsPerEvictionRun, idleObjects.size());
        }
        return (int) (Math.ceil(idleObjects.size() / Math.abs((double) numTestsPerEvictionRun)));
    }

2.8 evictionPolicyClassName

驱逐策略的类名;
默认值 DEFAULT_EVICTION_POLICY_CLASS_NAME = "org.apache.commons.pool2.impl.DefaultEvictionPolicy"

org.apache.commons.pool2.impl.GenericObjectPool

    public final void setEvictionPolicyClassName(final String evictionPolicyClassName) {
        try {
            Class<?> clazz;
            try {
                clazz = Class.forName(evictionPolicyClassName, true, Thread.currentThread().getContextClassLoader());
            } catch (final ClassNotFoundException e) {
                clazz = Class.forName(evictionPolicyClassName);
            }
            final Object policy = clazz.newInstance();
            if (policy instanceof EvictionPolicy<?>) {
                @SuppressWarnings("unchecked") // safe, because we just checked the class
                final EvictionPolicy<T> evicPolicy = (EvictionPolicy<T>) policy;
                this.evictionPolicy = evicPolicy;
            } else {
                throw new IllegalArgumentException("[" + evictionPolicyClassName + "] does not implement EvictionPolicy");
            }
        } catch (final ClassNotFoundException e) {
            throw new IllegalArgumentException("Unable to create EvictionPolicy instance of type " + evictionPolicyClassName, e);
        } catch (final InstantiationException e) {
            throw new IllegalArgumentException("Unable to create EvictionPolicy instance of type " + evictionPolicyClassName, e);
        } catch (final IllegalAccessException e) {
            throw new IllegalArgumentException("Unable to create EvictionPolicy instance of type " + evictionPolicyClassName, e);
        }
    }

2.9 testOnCreate

在创建对象时检测对象是否有效(true : 是) , 配置 true 会降低性能;
默认值 DEFAULT_TEST_ON_CREATE = false

org.apache.commons.pool2.impl.GenericObjectPool##borrowObject(final long borrowMaxWaitMillis)

    PooledObject<T> p = null;
    // ...省略
    // 配置true,新创建对象都会检测对象是否有效 【 create && getTestOnCreate() 】
    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;
            }
        }
    }

2.10 testOnBorrow

在从对象池获取对象时是否检测对象有效(true : 是) , 配置 true 会降低性能;
默认值 DEFAULT_TEST_ON_BORROW = false

org.apache.commons.pool2.impl.GenericObjectPool##borrowObject(final long borrowMaxWaitMillis)

    // 配置true,从对象池获取对象,总是要检测对象是否有效 【 getTestOnBorrow() 】
    if (p != null && (getTestOnBorrow() || create && getTestOnCreate())) {
        // ...省略
    }

2.11 testOnReturn

在向对象池中归还对象时是否检测对象有效(true : 是) , 配置 true 会降低性能;
默认值 DEFAULT_TEST_ON_RETURN = false

org.apache.commons.pool2.impl.GenericObjectPool##returnObject(final T obj)

    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;
        }
    }

2.12 testWhileIdle

在检测空闲对象线程检测到对象不需要移除时,是否检测对象的有效性。建议配置为 true,不影响性能,并且保证安全性;
默认值 DEFAULT_TEST_WHILE_IDLE = false

org.apache.commons.pool2.impl.GenericObjectPool##evict()

    final boolean testWhileIdle = getTestWhileIdle();
    // .... 代码省略
    // 配置为true, 检测空闲对象线程检测到对象不需要移除时,检测对象的有效性
    if (testWhileIdle) {
        boolean active = false;
        try {
            factory.activateObject(underTest);
            active = true;
        } catch (final Exception e) {
            destroy(underTest);
            destroyedByEvictorCount.incrementAndGet();
        }
        if (active) {
            if (!factory.validateObject(underTest)) {
                destroy(underTest);
                destroyedByEvictorCount.incrementAndGet();
            } else {
                try {
                    factory.passivateObject(underTest);
                } catch (final Exception e) {
                    destroy(underTest);
                    destroyedByEvictorCount.incrementAndGet();
                }
            }
        }
    }

2.13 timeBetweenEvictionRunsMillis

空闲连接检测的周期(单位毫秒);如果为负值,表示不运行检测线程;
默认值 DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS = -1L

org.apache.commons.pool2.impl.GenericObjectPool

    public GenericObjectPool(final PooledObjectFactory<T> factory, final GenericObjectPoolConfig config) {
           super(config, ONAME_BASE, config.getJmxNamePrefix());
        if (factory == null) {
            jmxUnregister(); // tidy up
            throw new IllegalArgumentException("factory may not be null");
        }
        this.factory = factory;
        
        idleObjects = new LinkedBlockingDeque<PooledObject<T>>(config.getFairness());

        setConfig(config);
        // 启动空闲连接检测线程
        startEvictor(getTimeBetweenEvictionRunsMillis());
    }

2.14 blockWhenExhausted

当对象池没有空闲对象时,新的获取对象的请求是否阻塞(true 阻塞,maxWaitMillis 才生效; false 连接池没有资源立马抛异常)
默认值 DEFAULT_BLOCK_WHEN_EXHAUSTED = true

org.apache.commons.pool2.impl.GenericObjectPool##borrowObject(final long borrowMaxWaitMillis)

    final boolean blockWhenExhausted = getBlockWhenExhausted();
    // ... 省略
    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");
        }
    } 

2.15 jmxEnabled

是否注册 JMX
默认值 DEFAULT_JMX_ENABLE = true

org.apache.commons.pool2.impl.BaseGenericObjectPool

    public BaseGenericObjectPool(final BaseObjectPoolConfig config, final String jmxNameBase, final String jmxNamePrefix) {
        if (config.getJmxEnabled()) {
            this.oname = jmxRegister(config, jmxNameBase, jmxNamePrefix);
        } else {
            this.oname = null;
        }

        // Populate the creation stack trace
        this.creationStackTrace = getStackTrace(new Exception());

        // save the current TCCL (if any) to be used later by the evictor Thread
        final ClassLoader cl = Thread.currentThread().getContextClassLoader();
        if (cl == null) {
            factoryClassLoader = null;
        } else {
            factoryClassLoader = new WeakReference<ClassLoader>(cl);
        }

        fairness = config.getFairness();
    }

2.16 jmxNamePrefix

JMX 前缀
默认值 DEFAULT_JMX_NAME_PREFIX = "pool"

org.apache.commons.pool2.impl.GenericObjectPool

    // JMX specific attributes
    private static final String ONAME_BASE = "org.apache.commons.pool2:type=GenericObjectPool,name=";

    public GenericObjectPool(final PooledObjectFactory<T> factory, final GenericObjectPoolConfig config) {
        // 参见上述 jmxEnabled 部分
        super(config, ONAME_BASE, config.getJmxNamePrefix());
        // .....
    }

2.17 jmxNameBase

使用 base + jmxNamePrefix + i 来生成 ObjectName
默认值 DEFAULT_JMX_NAME_BASE = nullGenericObjectPool 构造方法使用 ONAME_BASE 初始化。

    private ObjectName jmxRegister(final BaseObjectPoolConfig config,final String jmxNameBase, String jmxNamePrefix) {
        ObjectName objectName = null;
        final MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
        int i = 1;
        boolean registered = false;
        String base = config.getJmxNameBase();
        if (base == null) {
            base = jmxNameBase;
        }
        while (!registered) {
            try {
                ObjectName objName;
                if (i == 1) {
                    objName = new ObjectName(base + jmxNamePrefix);
                } else {
                    objName = new ObjectName(base + jmxNamePrefix + i);
                }
                mbs.registerMBean(this, objName);
                objectName = objName;
                registered = true;
            } catch (final MalformedObjectNameException e) {
                if (BaseObjectPoolConfig.DEFAULT_JMX_NAME_PREFIX.equals(jmxNamePrefix) && jmxNameBase.equals(base)) {
                    // 如果走到这步,就跳过jmx注册,应该不会发生
                    registered = true;
                } else {
                    // 前者使用的名称不是默认配置,则使用默认配置替代
                    jmxNamePrefix =
                            BaseObjectPoolConfig.DEFAULT_JMX_NAME_PREFIX;
                    base = jmxNameBase;
                }
            } catch (final InstanceAlreadyExistsException e) {
                // 增加一个索引,再试一次
                i++;
            } catch (final MBeanRegistrationException e) {
                // 如果走到这步,就跳过jmx注册,应该不会发生
                registered = true;
            } catch (final NotCompliantMBeanException e) {
                // 如果走到这步,就跳过jmx注册,应该不会发生
                registered = true;
            }
        }
        return objectName;
    }        

3. 子类GenericObjectPoolConfig配置参数

GenericObjectPoolConfig 是一个简单的配置类,封装了用于 GenericObjectPool 的配置。注意,该类也不是线程安全的,它仅用于在创建池时提供属性。


public class GenericObjectPoolConfig extends BaseObjectPoolConfig {
    
    private int maxTotal = DEFAULT_MAX_TOTAL;
    
    private int maxIdle = DEFAULT_MAX_IDLE;
    
    private int minIdle = DEFAULT_MIN_IDLE;
}

3.1 maxTotal

最大连接数,默认值 DEFAULT_MAX_TOTAL = 8

3.2 maxIdle

最大空闲连接数, 默认值 DEFAULT_MAX_IDLE = 8

3.3 minIdle

最小空闲连接数, 默认值 DEFAULT_MIN_IDLE = 0

总结

了解这些配置参数对于正确设置和管理 GenericObjectPool 至关重要,本篇的介绍基于commons-pool2-2.4.3,其他版本可能有出入,请自行查看。

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。