• 通用池化框架实践之GenericKeyedObjectPool


    前两天写了一篇文章介绍commons-pool2这个通用池化框架通用池化框架commons-pool2实践,其中提到了可以池化一个对象和一组对象,一个对象用到了GenericObjectPool这个类,一组对象用到了GenericKeyedObjectPool这个类。

    一开始我以为后者比较复杂,所以放弃了尝试,今天在写gRPC接口测试Demo,根据分片不同进行负载均衡连接不同节点的过程中,遇到了一个障碍。就是在服务调用gRPC的时候已经完成了自动负载均衡,我调用的SDK就需要自己实现根据不同分片连接不同的节点,这就用到了GenericKeyedObjectPool

    顾名思义,键值对象池。就是通过一个key对应一个对象类型来组合对象池,其本质上就是一个Mapkey是自定义,value就是org.apache.commons.pool2.ObjectPool,而但对象池化类GenericObjectPool也是实现了这个接口。

    经过查询源码注释有两点需要注意:

    1. Map的用的ConcurrentHashMap,是线程安全的。
    2. 获取和回收太频繁,会遇到性能问题。

    关于第二点,我有机会在做一期两者的性能测试。我现在用的是gRPC的连接对象io.grpc.ManagedChannel,而且每个类对象绑定的对象是io.grpc.stub.AbstractBlockingStub并不会场景去连接池中获取新连接,一个gRPC连接可以支撑N(资料称该值100左右,后续我计划50个线程公用一个连接)个线程的并发,所以暂时不用担心这个性能问题。

    根据上次文章的记录的顺序分成了三部分。

    可池化类

    首先我们需要一个可以被池化的对象,代码同上期文章。

    池化工厂类

    然后就是池化工厂类,这个类需要定义keyvalue的类型,然后就是照葫芦画瓢,跟上期文章一样。

    package com.funtester.funpool
    
    import com.funtester.base.interfaces.IPooled
    import org.apache.commons.pool2.BaseKeyedPooledObjectFactory
    import org.apache.commons.pool2.PooledObject
    /**
     * 可池化工厂类
     */
    abstract class KeyPoolFactory<F> extends BaseKeyedPooledObjectFactory<F, IPooled> {
    
        abstract IPooled init()
    
        @Override
        IPooled create(F k) throws Exception {
            return init()
        }
    
        @Override
        PooledObject<IPooled> wrap(IPooled obj) {
            return obj.reInit()
        }
    
        @Override
        void destroyObject(F key, PooledObject<IPooled> p) throws Exception {
            p.getObject().destory()
            super.destroyObject(key, p)
        }
    }
    
    

    这里提一嘴,com.funtester.funpool.KeyPoolFactory#destroyObject方法并不是必需的,如果池化的对象除了内存以外不需要额外的资源释放,就不用重写这个方法了。还有一种情况就是对象信息需要清除,比如org.apache.http.client.methods.HttpGet,需要把请求地址和请求头等信息清除,这个需要跟业务需求保持一致。不一定是全都清除。

    对象池

    照猫画虎,定义属性类型、配置项等等。

    package com.funtester.funpool
    
    import com.funtester.base.interfaces.IPooled
    import org.apache.commons.pool2.impl.GenericKeyedObjectPool
    import org.apache.commons.pool2.impl.GenericObjectPoolConfig
    
    class KeyPool {
    
        KeyPool(KeyPoolFactory factory) {
            this.factory = factory
            this.pool = init()
        }
    
        private GenericKeyedObjectPool<String, IPooled> pool = init();
    
        private KeyPoolFactory<String> factory
    
        private GenericKeyedObjectPool<String, IPooled> init() {
            // 连接池的配置
            GenericObjectPoolConfig poolConfig = new GenericObjectPoolConfig();
            // 池中的最大连接数
            poolConfig.setMaxTotal(8);
            // 最少的空闲连接数
            poolConfig.setMinIdle(0);
            // 最多的空闲连接数
            poolConfig.setMaxIdle(8);
            // 当连接池资源耗尽时,调用者最大阻塞的时间,超时时抛出异常 单位:毫秒数
            poolConfig.setMaxWaitMillis(-1);
            // 连接池存放池化对象方式,true放在空闲队列最前面,false放在空闲队列最后
            poolConfig.setLifo(true);
            // 连接空闲的最小时间,达到此值后空闲连接可能会被移除,默认即为30分钟
            poolConfig.setMinEvictableIdleTimeMillis(1000L * 60L * 30L);
            // 连接耗尽时是否阻塞,默认为true
            poolConfig.setBlockWhenExhausted(true);
            // 连接池创建
            return new GenericKeyedObjectPool<String, IPooled>(factory, poolConfig);
        }
    
    }
    
    
    

    然后我们就可以使用这个对象池了,我定义了两个方法来演示两种常见的场景:

    
        /**
         * 获取对象
         */
        IPooled get(String key) {
            try {
                return pool.borrowObject("FunTester");
            } catch (Exception e) {
                e.printStackTrace();
            }
            return factory.create("FunTester");
        }
    
        /**
         * 归还对象
         * @param iPooled
         */
        void back(String key, IPooled iPooled) {
            pool.returnObject("FunTester", iPooled)
        }
    
        /**
         * 执行器
         */
        def execute(String key, Closure closure) {
            IPooled client = get(key);
            try {
                closure(client);
            } finally {
                back(key, client);
            }
        }
    
    
    

    后续会放弃这种泛型的方式,因为泛型更加麻烦。等我再学习几天,再来测试这两个池化类的性能,为以后的使用提供参考依据。

    Have Fun ~ Tester !

  • 相关阅读:
    在Ubuntu下依然爱SOGO
    CompositePattern(23种设计模式之一)
    Arduino String.h库函数详解
    cp命令详解
    PHP AJAX 返回JSON 数据
    PHP AJAX返回 "TEXT"
    PHP JSON数据 AJAX
    PHP JQurey
    PHP 封装POD 类
    PHP 分页+查询
  • 原文地址:https://www.cnblogs.com/FunTester/p/16307063.html
Copyright © 2020-2023  润新知