通用池化框架 GenericKeyedObjectPool 性能测试 - 今日头条

本文由 简悦 SimpRead 转码, 原文地址 www.toutiao.com

线程数 执行次数 单线程 QPS 20 10 416 50 10 393 100 5 222 200 2 79 300 2 27。Have Fun

上次我测试了通用池化框架 GenericObjectPool 性能测试,效果还行,对后面使用 commons-pool2 框架的使用提供了非常有效的参考依据。

对于另外一个更复杂的 GenericKeyedObjectPool 也得安排上了,这就献上。

这部分内容与上期相同,这里不再赘述了。

这里用到了 org.apache.commons.pool2.BaseKeyedPooledObjectFactory<Integer, FunTesterPooled>,下面分享一下具体的代码。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/**
     * 池化工厂
     */
    private static class FunFactory extends BaseKeyedPooledObjectFactory<Integer, FunTesterPooled> {


        @Override
        FunTesterPooled create(Integer key) throws Exception {
            def pooled = new FunTesterPooled()
            pooled.setAge(key)
            return pooled
        }

        @Override
        PooledObject<FunTesterPooled> wrap(FunTesterPooled value) {
            return new DefaultPooledObject<FunTesterPooled>(value)
        }

        @Override
        void destroyObject(Integer key, PooledObject<FunTesterPooled> p, DestroyMode destroyMode) throws Exception {
            p.getObject().setAge(0)//资源回收
            super.destroyObject(key, p, destroyMode)
        }
    }

虽然这个 GenericKeyedObjectPool 理论上是可以存储不同的对象的,但是这个在创建的时候还是需要确定一个可池化对象类型。所以后面所有创建的对象必需这个可池化对象类或者子类。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
/**
     * 初始化对象池
     * @return
     */
    static def initPool() {
        def config = new GenericKeyedObjectPoolConfig<FunTesterPooled>()
        config.setMaxTotal(thread * 2)
        config.setMinIdlePerKey(10)
        config.setMaxIdlePerKey(100)
        config.setMaxWait(Duration.ofMillis(1000))
        config.setMaxIdlePerKey(thread)
        config.setMaxIdlePerKey(10)
        config.setMinIdlePerKey(2)
        return new GenericKeyedObjectPool<Integer, FunTesterPooled>(new FunFactory(), config)
    }

这里的设置分成了两类,就是每个对象池和总对象池的各类参数设置。

跟上期的用例很像,只是请求参数增加了 key,这里 return 的时候也需要增加 key 参数。测试用例如下:

 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
static GenericKeyedObjectPool<Integer, FunTesterPooled> pool

    static def desc = "池化框架性能测试"

    static int times = 200

    static int thread = 3

    public static void main(String[] args) {
        this.pool = initPool()
        ThreadBase.COUNT = true
        RUNUP_TIME = 0
        POOL_SIZE = thread
        thread.times {
            pool.addObjects(it, thread)
        }
        output("对象创建完毕 创建数量${pool.getNumIdle()}")
        new Concurrent(new FunTester(), thread, desc).start()
        pool.close()
    }

    private static class FunTester extends FixedThread {


        FunTester() {
            super(null, times, true)
        }

        @Override
        protected void doing() throws Exception {
            def randomInt = getRandomInt(thread)
            def object = pool.borrowObject(randomInt)
            pool.returnObject(randomInt, object)
        }

        @Override
        FunTester clone() {
            return new FunTester()
        }
    }

用例设计也是跟上期的文章一样,为了尽可能有对比价值,使用了尽可能相同的参数。

线程数 执行次数(万) QPS 1 300 189501 2 300 322603 5 300 120334 10 100 96861 20 50 81440

可以看出,一旦线程数增加,QPS 就是降低非常快,线程较低的时候性能跟 GenericObjectPool 相差无几,这很可能是 java.util.concurrent.ConcurrentHashMap 导致的。有时间我再对 java.util.concurrent.ConcurrentHashMap 进行性能测试。

使用了休眠 2ms 的配置。

线程数 执行次数 (k) 单线程 QPS 20 10 416 50 10 393 100 5 222 200 2 79 300 2 27

后面就不测了,再测可能回归个位数了。结论如上,线程数增加的的话,粗略估计 100 以上,org.apache.commons.pool2.impl.GenericKeyedObjectPoolConfig 的性能就会变得奇差无比,可能是多种原因导致的。更不用提线程数增加 500 以后,会遇到 org.apache.commons.pool2.impl.LinkedBlockingDeque 和 java.util.concurrent.atomic.AtomicLong 性能大幅下降。看来以后还是不能简单使用对象池代替创建对象。