java使用commons-pool2创建对象池


一、对象池是什么?

对象池模式经常用在频繁创建、销毁对象(并且对象创建、销毁开销很大)的场景,比如数据库连接池、线程池、任务队列池等。
使用对象池调用对象时,不使用常规的new 构造子的方式,而是通过一个对象池操作。即如果池中存在该对象,则取出;如果不存在,则新建一个对象并存储在池中。当使用完该对象后,则将该对象的归还给对象池。

二、使用步骤

1.引入库

代码如下(示例):

gradle:

compile group: 'org.apache.commons', name: 'commons-pool2', version: 2.10.0

maven:

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

2.对象实体

以下例子的对象为我实际某个项目中的实体,属性内容的意义可忽略

package com.iscas.sp.proxy.model;

import com.iscas.sp.filter.SpChain;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.HttpHeaders;
import io.netty.handler.codec.http.HttpResponse;
import lombok.Data;
import lombok.experimental.Accessors;

import java.io.InputStream;
import java.util.List;
import java.util.Map;

/**
 *
 * @author zhuquanwen
 * @vesion 1.0
 * @date 2021/7/11 19:20
 * @since jdk1.8
 */
@Data
@Accessors(chain = true)
public class SpContext {
    private SpChain spChain = new SpChain();
    /**当前所处的状态:pre 代理前,post 代理后*/
    private String type = "pre";
    /**netty的ctx*/
    private ChannelHandlerContext ctx;
    /**请求uri*/
    private String uri;
    /**请求参数*/
    private Map<String, List<String>> params;
    /**path*/
    private String path;
    /**是否需要keepalive*/
    private boolean keepAlive;
    /**请求*/
    private FullHttpRequest request;
    /**响应*/
    private HttpResponse response;
//    /**请求头*/
//    private Map<String, String> requestHeaders = new HashMap<>();
//    /**响应头*/
//    private Map<String, String> responseHeaders = new HashMap<>();

    /**请求头*/
    private HttpHeaders reqHeaders;
    /**响应头*/
    private HttpHeaders resHeaders;

    /**set-cookie*/
    private List<Cookie> setCookies;

    /**服务路由信息*/
    private ServerInfo serverInfo;

    /**当前代理的协议包括http/s、file*/
    private String proxyType = "http/s";

    /**返回值的流*/
    private InputStream resIs;

}

3.生产对象的工厂

package com.iscas.sp.proxy.model.pool;

import com.iscas.common.tools.core.reflect.ReflectUtils;
import com.iscas.sp.filter.FilterFactory;
import com.iscas.sp.filter.IFilter;
import com.iscas.sp.filter.SpChain;
import com.iscas.sp.proxy.base.Constant;
import com.iscas.sp.proxy.model.Cookie;
import com.iscas.sp.proxy.model.ServerInfo;
import com.iscas.sp.proxy.model.SpContext;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.HttpHeaders;
import io.netty.handler.codec.http.HttpResponse;
import org.apache.commons.pool2.BasePooledObjectFactory;
import org.apache.commons.pool2.PooledObject;
import org.apache.commons.pool2.impl.DefaultPooledObject;
import org.apache.commons.pool2.impl.GenericObjectPool;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;

import java.io.InputStream;
import java.lang.reflect.Field;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicInteger;

/**
 *
 * @author zhuquanwen
 * @vesion 1.0
 * @date 2021/8/4 15:26
 * @since jdk1.8
 */
public class SpContextFactory extends BasePooledObjectFactory<SpContext> {
    private static volatile GenericObjectPool<SpContext> pool = null;
    public static GenericObjectPool<SpContext> getInstance() {
        if (pool == null) {
            synchronized (SpContextFactory.class) {
                if (pool == null) {
                    GenericObjectPoolConfig poolConfig = new GenericObjectPoolConfig();
                    poolConfig.setMaxTotal(-1);
                    poolConfig.setMaxIdle(Constant.PROXY_CONF.getMaximumPoolSize());
                    poolConfig.setMinIdle(Constant.PROXY_CONF.getCorePoolSize());
                    poolConfig.setLifo(false);
                    pool = new GenericObjectPool<>(new SpContextFactory(), poolConfig);
                }
            }
        }
        return pool;
    }

    @Override
    public SpContext create() throws Exception {
        return new SpContext();
    }

    @Override
    public PooledObject<SpContext> wrap(SpContext obj) {
        return new DefaultPooledObject<>(obj);
    }

    public static void clear() {
        getInstance().clear();
    }

    public static void close() {
        getInstance().close();
    }

    public static SpContext borrowObject() throws Exception {
        return getInstance().borrowObject();
    }

    public static void returnObject(SpContext context) throws Exception {
        getInstance().returnObject(context);
    }

    @Override
    public void passivateObject(PooledObject<SpContext> p) throws Exception {
        SpContext spContext = p.getObject();
        //将对象的数据都设置为null
        ReflectUtils.initObj(spContext, "spChain");
        //spChain单独处理,设置其值为初始化值
        SpChain spChain = spContext.getSpChain();
        Field[] declaredFields = spChain.getClass().getDeclaredFields();
        for (Field declaredField : declaredFields) {
            declaredField.setAccessible(true);
            if (Objects.equals("index", declaredField.getName())) {
                AtomicInteger index = (AtomicInteger) declaredField.get(spChain);
                index.set(0);
            } else if (Objects.equals("preFinished", declaredField.getName())) {
                declaredField.set(spChain, false);
            } else if (Objects.equals("postFinished", declaredField.getName())) {
                declaredField.set(spChain, false);
            }
//            else if (Objects.equals("filters", declaredField.getName())) {
//                declaredField.set(spChain, FilterFactory.getFilters(Constant.PROXY_CONF));
//            }
        }
        spContext.setType("pre");
        spContext.setProxyType("http/s");
    }
}

注意这里重写passivateObject的目的是在对象归还时为它初始化一些值。

4.使用对象池

以下代码是示例,重要的两行:
1.从对象池获取对象:spContext = SpContextFactory.borrowObject();
2.用完归还对象:SpContextFactory.returnObject(spContext);

SpContext spContext = null;
                    try {
                        spContext = SpContextFactory.borrowObject();
                        //封装context
                        spContext.setCtx(ctx);
                        wrapperContext(spContext, (FullHttpRequest) msg);
                        Constant.spContextThreadLocal.set(spContext);
                        //执行过滤器
                        SpChain spChain = spContext.getSpChain();
                        spChain.doFilter(spContext);
                        //如果preFinished为false,说明过滤器在中间停止了,不继续进行代理
                        if (spChain.isPreFinished()) {
                            httpHandler.handleHttpRequest();
                        }
                    } catch (Throwable e) {
                        //异常统一处理
                        HttpUtils.httpExceptionHandler(e);
                    } finally {
                        //清除threadlocal
                        Constant.spContextThreadLocal.remove();
                        try {
                            SpContextFactory.returnObject(spContext);
                        } catch (Exception exception) {
                            exception.printStackTrace();
                        }
                        HttpUtils.release(ctx, msg);
                    }

5.补充第三步代码中用到的ReflectUtils#initObj

 /**
     * 初始化对象
     */
    public static void initObj(Object obj, String... ignoreFields) throws IllegalAccessException {
        if (obj == null) {
            return;
        }
        if (obj instanceof Map || obj instanceof Collection ||
                obj.getClass().isArray()) {
            throw new RuntimeException("仅支持JavaBean的初始化");
        }
        Class<?> aClass = obj.getClass();
        Field[] declaredFields = aClass.getDeclaredFields();
        if (declaredFields != null) {
            label: for (Field declaredField : declaredFields) {
                if (ignoreFields != null) {
                    for (String ignoreField : ignoreFields) {
                        if (Objects.equals(declaredField.getName(), ignoreField)) {
                            continue label;
                        }
                    }
                }
                declaredField.setAccessible(true);
                Object val = null;
                Class<?> type = declaredField.getType();
                if (type == int.class) {
                    val = 0;
                } else if (type == long.class) {
                    val = 0L;
                } else if (type == short.class) {
                    val = 0;
                } else if (type == byte.class) {
                    val = 0;
                } else if (type == float.class) {
                    val = 0f;
                } else if (type == double.class) {
                    val = 0d;
                } else if (type == char.class) {
                    val = ' ';
                } else if (type == boolean.class) {
                    val = false;
                } else {
                    val = null;
                }
                declaredField.set(obj, val);
            }
        }

    }

总结

以上介绍了对象池的概念,通过common-pool2包实现了对象池,并测试使用。 使用对象池有很多好处,但也得应用场景,优缺点总结如下:

对象池的优点
复用池中对象,消除创建对象、回收对象 所产生的内存开销、cpu开销以及(若跨网络)产生的网络开销.
常见的使用对象池有:在使用socket时(包括各种连接池)、线程等等

对象池的缺点:
现在Java的对象分配操作不比c语言的malloc调用慢, 对于轻中量级的对象, 分配/释放对象的开销可以忽略不计;
并发环境中, 多个线程可能(同时)需要获取池中对象, 进而需要在堆数据结构上进行同步或者因为锁竞争而产生阻塞, 这种开销要比创建销毁对象的开销高数百倍;
由于池中对象的数量有限, 势必成为一个可伸缩性瓶颈;
很难正确的设定对象池的大小, 如果太小则起不到作用, 如果过大, 则占用内存资源高,
对象池有其特定的适用场景:
受限的, 不需要可伸缩性的环境(cpu\内存等物理资源有限): cpu性能不够强劲, 内存比较紧张, 垃圾收集, 内存抖动会造成比较大的影响, 需要提高内存管理效率, 响应性比吞吐量更为重要;
数量受限的资源, 比如数据库连接;
创建成本高昂的对象, 可斟酌是否池化, 比较常见的有线程池(ThreadPoolExecutor), 字节数组池等;

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,针对上述代码,我们可以使用commons-pool2提供的对象池进行修改。具体实现步骤如下: 1. 引入commons-pool2的依赖包,例如在Maven项目中,在pom.xml文件中添加以下代码: ``` <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-pool2</artifactId> <version>2.11.1</version> </dependency> ``` 2. 创建一个连接池对象,用于管理HttpClients对象的创建和回收: ```java GenericObjectPool<CloseableHttpClient> httpClientPool = new GenericObjectPool<>(new HttpClientFactory()); ``` 其中,HttpClientFactory是一个实现了PooledObjectFactory接口的类,用于创建和销毁HttpClients对象。具体实现可以参考下面的代码: ```java public class HttpClientFactory implements PooledObjectFactory<CloseableHttpClient> { @Override public PooledObject<CloseableHttpClient> makeObject() throws Exception { return new DefaultPooledObject<>(HttpClients.createDefault()); } @Override public void destroyObject(PooledObject<CloseableHttpClient> pooledObject) throws Exception { CloseableHttpClient httpClient = pooledObject.getObject(); httpClient.close(); } @Override public boolean validateObject(PooledObject<CloseableHttpClient> pooledObject) { CloseableHttpClient httpClient = pooledObject.getObject(); return httpClient != null && httpClient.getConnectionManager().isShutdown(); } @Override public void activateObject(PooledObject<CloseableHttpClient> pooledObject) throws Exception { // do nothing } @Override public void passivateObject(PooledObject<CloseableHttpClient> pooledObject) throws Exception { // do nothing } } ``` 3. 使用连接池对象获取HttpClients对象,例如: ```java CloseableHttpClient httpClient = httpClientPool.borrowObject(); ``` 4. 使用完HttpClients对象后,将其归还给连接池: ```java httpClientPool.returnObject(httpClient); ``` 通过上述步骤,我们就可以使用commons-pool2提供的对象池来管理HttpClients对象的创建和回收了。这样可以大大减少对象的创建和销毁次数,提高程序的性能和稳定性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值