SpringBoot + thrift + apache.commons.pool2:在springboot项目上构建thrift客户端连接池

为君一笑 提交于 2020-03-06 16:41:53

一、前言

因为工作需要,用thrift实现rpc的时候,考虑到创建、销毁连接的开销比较大,因此想到弄一个thrift客户端连接池,每次使用thrift客户端只需要从池子中获取一个连接,用完后再放回去,这样可以保证程序重复使用少数的几个连接而不需要每次访问都创建和销毁连接, 从而提高系统性能。

网上有部分用org.apache.commons.pool2构建thrift客户端连接池的方法,因为我对springboot不够熟悉,在整合三者的时候遇到点困难,好在最后解决了,在此做个记录。

二、thrift服务端

这部分内容比较简单,网上教程很多,这里简单提一下。
首先是编写thrift文件

struct DebugResponse{
    1: required i32 code;
    2: required string message;
}

service DebugService{
    DebugResponse debug(1:string id,2:string data);
}

然后用thrift.exe自动生成java文件,放在项目中,然后编写业务逻辑

public class DebugServiceImpl implements DebugService.Iface {
    private Logger logger = LoggerFactory.getLogger(DebugServiceImpl.class);

    @Override
    public DebugResponse debug(String id, String data) throws TException {
    	//这里实现你的业务逻辑
        logger.info("get a new request");
        return new DebugResponse(0, id + "*-*-*" + data);
    }
}

然后编写服务端启动程序

	public static void main(String[] args) {
        try {
            TProcessor tProcessor = new DebugService.Processor<DebugService.Iface>(new DebugServiceImpl());
            TNonblockingServerSocket socket = new TNonblockingServerSocket(6868);
            TThreadedSelectorServer.Args args = new TThreadedSelectorServer.Args(socket);
            args.processor(tProcessor);
            args.transportFactory(new TFramedTransport.Factory());
            args.protocolFactory(new TBinaryProtocol.Factory());
            TServer server = new TThreadedSelectorServer(args);
        } catch (Exception e) {

        }
        System.out.println("debug server start...");
        server.serve();
    }

三、thrift客户端连接池

网上的方法大体相似,我这里加上了springboot的一些特性

引入pom依赖

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

需要池化的对象

需要池化的对象是thrift的TTransport

对象工厂

创建工厂只需要继承BasePooledObjectFactory,并重写部分函数。
因为每次创建TTransport时,要指定ip和端口等参数,因此这里使用springboot的自动注入功能@ConfigurationProperties(prefix = "debug.thrift"),这样的话不用在代码里写死配置,可以通过在配置文件app.properties里加上debug.thrift.host=XX.XX.XX.XX等完成配置。

@ConfigurationProperties(prefix = "debug.thrift")
@Component
public class DebugConnectionFactory extends BasePooledObjectFactory<TTransport> {

    private String host = "localhost";
    private int port = 6868;
    private int timeOut = 10000;

    public void setHost(String host) {
        this.host = host;
    }

    public void setPort(int port) {
        this.port = port;
    }

    public void setTimeOut(int timeOut) {
        this.timeOut = timeOut;
    }

    @Override
    public TTransport create() throws Exception {
        TTransport tTransport = new TFramedTransport(new TSocket(host, port, timeOut));
        if (!tTransport.isOpen()) {
            tTransport.open();
        }
        return tTransport;
    }

    @Override
    public boolean validateObject(PooledObject<TTransport> p) {
        return p.getObject().isOpen();
    }

    @Override
    public PooledObject<TTransport> wrap(TTransport tTransport) {
        return new DefaultPooledObject<>(tTransport);
    }

    @Override
    public void destroyObject(PooledObject<TTransport> p) {
    //这里的作用是保证客户端在关闭时,服务端不会报错
        if (p.getObject().isOpen()) {
            p.getObject().close();
        }
    }
}

app.properties配置文件

debug.thrift.host=XX.XX.XX.XX
debug.thrift.port=12321
debug.thrift.timeOut=30000

对象池

直接继承了GenericObjectPool这个类,因为我们池化的对象是TTransport,而我们更希望获得Client,因此加上获得和回收的两个方法

public class DebugConnectionPool extends GenericObjectPool<TTransport> {
    public DebugConnectionPool(PooledObjectFactory<TTransport> factory) {
        super(factory);
    }

    public DebugConnectionPool(PooledObjectFactory<TTransport> factory, GenericObjectPoolConfig config) {
        super(factory, config);
    }

    public DebugConnectionPool(PooledObjectFactory<TTransport> factory, GenericObjectPoolConfig config, AbandonedConfig abandonedConfig) {
        super(factory, config, abandonedConfig);
    }
    
    //获得
    public DebugService.Client getClient() throws Exception {
        TTransport tTransport = this.borrowObject();
        TProtocol tProtocol = new TBinaryProtocol(tTransport);
        return new DebugService.Client(tProtocol);
    }

	//回收
    public void releaseConnection(DebugService.Client client) {
        TTransport tTransport = client.getInputProtocol().getTransport();
//        TTransport tTransport = client.getOutputProtocol().getTransport();
        //因为我们声明的时候是new DebugService.Client(tProtocol),这里两个protocol是一样的,选一个就可
        this.returnObject(tTransport);
    }

}

对象池的配置类

从上面的构造方法里可以看到,对象池除了传入一个工厂外,还可以传入一个配置类,所以我继承了GenericObjectPoolConfig对象池配置类

@ConfigurationProperties(prefix = "debug.pool")
@Component
public class PoolConfig extends GenericObjectPoolConfig {
}

跟前面一样,因为加上了@ConfigurationProperties(prefix = "debug.pool"),所以可以在配置文件app.properties里加上配置,在写配置文件的时候idea也会提示我们哪些属性可以配置。
poolConfig自动配置

Spring配置类

要在Spring中使用DebugConnectionPool这个对象池,还得加个配置类,声明一个@Bean

@Configuration
public class PoolAutoConfiguration {
    @Bean
    public DebugConnectionPool debugConnectionPool(PoolConfig poolConfig, DebugConnectionFactory debugConnectionFactory) {
    //这里一定要这么设置,不然springboot启动会报已经注册了某个bean的错误
        poolConfig.setJmxEnabled(false);
        return new DebugConnectionPool(debugConnectionFactory, poolConfig);
    }
}

四、使用

至此,就完成了所有的准备工作了,要想使用,只需要注入DebugConnectionPool即可

	@Autowired
	DebugConnectionPool debugConnectionPool;
	@Test
	public void testPool() throws Exception {
	    DebugService.Client client = debugConnectionPool.getClient();
	    DebugResponse debug = client.debug("test_id", "data");
	    System.out.println(debug.toString());
	    debugConnectionPool.releaseConnection(client);
	}

五、总结

其实有很多人写了thrift客户端连接池的教程,但是我满脑子就是想用上springboot自动注入配置的特性,所以折腾了一下午,最终结果虽然功能简单,但还算满意。

我自己很少写博客,写的不对的地方欢迎指出。

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!