基于Apache-Commons-Pool2实现Grpc客户端链接池

概述

在项目运行过程当中,有些操做对系统资源消耗较大,好比创建数据库链接、创建Redis链接等操做,咱们但愿一次性建立多个链接对象,并在之后须要使用时能直接使用已建立好的链接,达到提升性能的目的。池技术经过提早将一些占用较多资源的对象初始化,并将初始化后的对象保存到池中备用,达到提升应用服务性能的目的,数据库的JDBC链接池和Jedis链接池等都使用了池技术。
Apache-Commons-Pool2提供了一套池技术的规范接口和实现的通用逻辑,咱们只须要实现其抽象出来的方法就能够了。这篇博文主要分享基于Apache-Commons-Pool2来实现Grpc链接池的应用。
关于Grpc相关的内容,你们如想了解基本的实现方法,能够参考个人另外一篇博客(传送门):http://www.javashuo.com/article/p-epfzshdk-hz.html数据库

核心组件

咱们先来了解一下Apache-Commons-Pool2规范接口中涉及到的几个核心组件,包括:apache

  • ObjectPool
    对象池,用于存储对象,并管理对象的入池和出池。对象池的实现类是 GenericObjectPool<T>;
  • PoolConfig
    池属性,用于设置链接池的一些配置信息,好比最大池容量、超过池容量后的处理逻辑等。池属性的实现类是:GenericObjectPoolConfig;
  • ObjectFactory
    对象工厂,在须要的时候生成新的对象实例,并放入池中。对象工厂的接口是:interface PooledObjectFactory<T>;
  • ClientObject
    池对象,由对象工厂负责建立,并放入到对象池中;须要使用时从对象池中取出,执行对象的业务逻辑,使用完后再放回对象池。池对象的接口是:interface PooledObject<T>。

    核心组件依赖关系及其工做流程

    接口与类之间的依赖关系

    在梳理链接池相关的核心组件工做流程以前,咱们先来了解一下核心组件涉及到的类和接口之间的继承和实现关系。架构

  • 对象池类的继承关系
    对象池的最顶层接口是ObjectPool<T>,里面定义了对象池的基本方法,包括对象的添加、取出、校验、返还,以及获取处于Idle休眠状态的对象数量、获取处于Active状态的对象数量、清空池、关闭池。
    抽象类BaseGenericObjectPool<T>,定义了对象池的初始配置,并实现了对象池的基本接口方法。
    池类GenericObjectPool<T>继承了抽象类BaseGenericObjectPool<T>,并实现了ObjectPool<T>接口。其中添加了对象工厂、存储全部对象的Map、存储Idle对象的链式阻塞队列、当前已建立的对象数等属性。
    因为GenericObjectPool<T>类支持范型,咱们要作的,就是指定GenericObjectPool<T>池类返回的池对象类型<T>,并设置对象工厂类、配置类等池属性;或者继承GenericObjectPool类以添加更多的自定义池特性。
    基于Apache-Commons-Pool2实现Grpc客户端链接池并发

  • 池属性类的继承关系
    池属性的最上层接口是interface Cloneable,抽象类BaseObjectPoolConfig实现了这个接口,并定义了默认的池配置属性。
    GenericObjectPoolConfig类继承了BaseObjectPoolConfig,一样定义了默认的池配置属性值。
    咱们能够直接使用GenericObjectPoolConfig类,或者继承GenericObjectPoolConfig类,根据本身的需求设置自定义池配置属性。
    基于Apache-Commons-Pool2实现Grpc客户端链接池ide

  • 池内对象类的继承关系
    池内对象类实现了上层的PooledObject<T>接口,这个接口里面定义了一个池对象须要实现的各类方法。
    另外,池内对象类还须要定义类自己须要具有的成员属性和须要实现的业务方法。
    基于Apache-Commons-Pool2实现Grpc客户端链接池性能

  • 对象工厂类的继承关系
    对象工厂类实现了最上层的PooledObjectFactory<T>接口,该接口定义了对象工厂的核心功能方法,包括:建立对象、销毁对象、校验对象、激活对象、钝化对象。
    基于Apache-Commons-Pool2实现Grpc客户端链接池

工做流程

根据上述对核心组件的类继承关系分析,咱们能够梳理出一个流程,逐步实现各个组件,并组合成一套适用于咱们业务的链接池架构。咱们来看看这个流程该如何定义。
(1)定义咱们的池内对象类 ClientObject,并结合咱们的实际业务来实现上层接口的方法。
(2)定义对象工厂类ClientFactory,并结合咱们的实际业务来实现上层接口的方法。
(3)定义池属性类ClientPoolConfig,结合咱们的实际需求来设置属性值。
(4)使用对象池GenericObjectPool,指定泛型类型GenericObjectPool<ClientObject>。测试

基于Apache-Commons-Pool2实现Grpc客户端链接池

链接池内部的核心业务逻辑:
基于Apache-Commons-Pool2实现Grpc客户端链接池线程

池内对象的建立和返回逻辑是池技术里的关键,能够查看池对象的borrowObject方法去了解这部分细节内容。code

应用实践

代码实现

根据上述对Apache-commons-pool2的特色和实现流程的分析,咱们基于Grpc客户端链接池的应用场景,来进行代码实践,主要包括实现池内对象类 ClientObject和实现对象工厂类ClientFactory。
具体代码能够进入个人百度网盘下载,连接以下:
https://pan.baidu.com/s/1eaGpz6XN2a3ssw0eYsNLww对象

代码测试

为了验证咱们的Grpc链接池的做用,我编写了一个测试方法,模拟如下场景,即开启10个线程,每一个线程循环10次使用Grpc链接发送消息给grpc服务端,而后查看线程池中累计建立的链接对象个数、线程池中每一个链接对象的被使用次数等信息。
经过测试输出的信息,我获得的结论是:不使用链接池时,总共须要进行100次Grpc链接并发送消息;使用链接池后,总共仅须要创建2次Grpc链接来发送100次消息,每一个链接被调用了50次。
测试代码以下。

package com.cmcc.littlec.grpc.poolclient;
import com.cmcc.littlec.grpc.util.Constants;
import org.apache.commons.pool2.impl.GenericObjectPool;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;

public class test {
    @SuppressWarnings("unchecked")
    public static GenericObjectPool<ClientObject> getClientPool(){
        ClientPoolFactory factory = new ClientPoolFactory(Constants.grpcHost, Constants.grpcPort);
        GenericObjectPoolConfig config = new GenericObjectPoolConfig();
        config.setMaxIdle(8);
        config.setMinIdle(3);
        config.setMaxTotal(18);
        config.setTestOnBorrow(true);
        config.setTestOnReturn(true);
        GenericObjectPool<ClientObject> clientPool = new GenericObjectPool<ClientObject>(factory, config);
        return clientPool;
    }

    public static void  main(String[] args ){
        final GenericObjectPool<ClientObject> clientPool = getClientPool();
        for(int i=0; i<10; i++) {
            Thread t = new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        for (int i = 0; i < 10; i++) {
                            ClientObject client = clientPool.borrowObject();                                                        
                            String str = "hello, grpc client_" + i;//参数
                            try {
                                client.sayHello(str);
                            }catch(Exception e){
                                client.invalidate();
                            }
                            System.out.println("Thread : " + Thread.currentThread().getName() + "; clientPool size : " + clientPool.getCreatedCount());
                            System.out.println("clientObj : "+client.toString());
                            clientPool.returnObject(client);
                        }
                    } catch (Exception e) {
                        System.out.println(e.getMessage());
                    }
                }
            });
            t.start();
            try {
                if(i%2==0) {
                    Thread.sleep(5000L);//每隔两个线程建立后停顿5S
                }
            }catch(Exception e){ }
        }
    }
}
相关文章
相关标签/搜索