前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >通用池化框架commons-pool2实践

通用池化框架commons-pool2实践

作者头像
FunTester
发布2022-05-17 17:02:36
5880
发布2022-05-17 17:02:36
举报
文章被收录于专栏:FunTesterFunTester

最近在学习使用gRPC的知识过程中,突然发现了gRPC并没有提供一个类似于HttpClient连接池管理的功能,所以搜了一下相关资料,然后发现了一个通用的池化框架commons-pool2

从Go语言说起

对于池化技术相信大家都经说过,对于Java来讲:线程池,对于HttpClient:连接池。之前我是一直只是使用,第一次见证到池化技术的威力是在学习Go语言的HTTP接口测试常用的两个库:net/httpfasthttp

下面是net/http创建HTTP请求的方法封装:

代码语言:javascript
复制
// Get 获取GET请求
// @Description:
// @param uri
// @param args
// @return *fhttp.Request
func Get(uri string, args map[string]interface{}) *http.Request {
 if args != nil {
  uri = uri + "?" + ToValues(args)
 }
 request, _ := http.NewRequest("GET", uri, nil)
 return request
}

下面是fasthttp的创建HTTP请求的方法封装:

代码语言:javascript
复制
func DoGet(url string, args map[string]interface{}) ([]byte, error) {
 req := fasthttp.AcquireRequest()
 defer fasthttp.ReleaseRequest(req) // 用完需要释放资源
 req.Header.SetMethod("GET")
 values := ToValues(args)
 req.SetRequestURI(url + "?" + values)
 resp := fasthttp.AcquireResponse()
 defer fasthttp.ReleaseResponse(resp) // 用完需要释放资源
 if err := FastClient.Do(req, resp); err != nil {
  fmt.Println("请求失败:", err.Error())
  return nil, err
 }
 return resp.Body(), nil
}

其中fasthttp两行:

代码语言:javascript
复制
req := fasthttp.AcquireRequest()
defer fasthttp.ReleaseRequest(req) 

resp := fasthttp.AcquireResponse()
defer fasthttp.ReleaseResponse(resp)

这就是常用的池化技术使用规范,先获取一个,然后用完之后还回去。

据资料显示,两者性能差异最大的原因就是fasthttp采用了对象池化技术。一下勾起我的好奇心,不过并没有采取行动,还以为是Go语言的奇技淫巧。后来想曾经想过自己实现一个对象池,后来由于技术不够放弃了,原因是性能测试框架已经满足了设计标准,有了阶段性的成果。

commons-pool2

Apache Commons Pool库提供了一整套用于实现对象池化的API,以及若干种各具特色的对象池实现。2.0版本,并非是对1.x的简单升级,而是一个完全重写的对象池的实现,显著的提升了性能和可伸缩性,并且包含可靠的实例跟踪和池监控。

这个是偶然发现的,没想到真实我孤陋寡闻了。既然挂上了Apache名字,就知道这是一个非常成熟的框架,所以果断学习起来。首先从gRPC的测试代码中剥离。我把使用分成了三部分。

可池化类

首先我们需要一个可以被池化的对象,也可以是一组对象,这里我只分享前者。我写了一个接口com.funtester.base.interfaces.IPooled

代码语言:javascript
复制
package com.funtester.base.interfaces

import org.apache.commons.pool2.PooledObject

interface IPooled {

    PooledObject<IPooled> reInit()

    void destory()

}

池化工厂类

然后我们需要一个池化工厂类,这个类主要解决如何创建可池化对象,如何将池化对象包装成org.apache.commons.pool2.PooledObject,这个是对象池直接存储的对象,还有一个摧毁的com.funtester.funpool.FunPoolFactory#destroyObject方法。

代码语言:javascript
复制
package com.funtester.funpool

import com.funtester.base.interfaces.IPooled
import org.apache.commons.pool2.BasePooledObjectFactory
import org.apache.commons.pool2.PooledObject

/**
 * 可池化工厂类
 */
abstract class FunPoolFactory extends BasePooledObjectFactory<IPooled> {

    abstract IPooled init()

    @Override
    IPooled create() throws Exception {
        init()
    }

    @Override
    PooledObject<IPooled> wrap(IPooled obj) {
        return obj.reInit()
    }

    @Override
    void destroyObject(PooledObject<IPooled> p) throws Exception {
        p.getObject().destory()
        super.destroyObject(p)
    }
}

对象池

这个算是简单的,设置几个常用的配置项,然后创建对象池。

代码语言:javascript
复制
package com.funtester.funpool

import com.funtester.base.interfaces.IPooled
import org.apache.commons.pool2.impl.GenericObjectPool
import org.apache.commons.pool2.impl.GenericObjectPoolConfig

class FunPool {

    private static GenericObjectPool<IPooled> pool = init();

    private static FunPoolFactory factory

    private static GenericObjectPool<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 GenericObjectPool<>(factory, poolConfig);
    }

}

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

代码语言:javascript
复制
    /**
     * 从连接池获取对象
     */
    static IPooled get() {
        try {
            return pool.borrowObject();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return factory.create();
    }

    /**
     * 执行器
     */
    static def execute(Closure closure) {
        IPooled client = get();
        try {
            closure(client);
        } finally {
            pool.returnObject(client);
        }
    }

当然这个演示的Demo是非常不优雅的,而且缺少拓展性,后面我会继续优化。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2022-05-12,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 FunTester 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 从Go语言说起
  • commons-pool2
    • 可池化类
      • 池化工厂类
        • 对象池
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档