SpringBoot + thrift + apache.commons.pool2: build a thrift client connection pool on the springboot project

Keywords: Spring SpringBoot Apache socket

Catalog

I. Preface

Because of the need of work, when using thrift to realize rpc, considering that the cost of creating and destroying connections is relatively large, I think of making a connection pool for thrift clients. Each time I use thrift clients, I only need to get a connection from the pool, and then put it back when I use up, so that the program can reuse a few connections without creating and destroying connections every time I visit To improve system performance.

On the Internet, there are some ways to build a thrift client connection pool with org.apache.commons.pool2. Because I am not familiar with springboot, I have some difficulties in integrating the three. Fortunately, I solved them at the end. Here is a record.

2, thrift server

This part of the content is relatively simple, there are many online tutorials, here is a brief mention.
First, write a thrift file

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

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

Then use thrift.exe to automatically generate java files, put them in the project, and write business logic

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

    @Override
    public DebugResponse debug(String id, String data) throws TException {
    	//Realize your business logic here
        logger.info("get a new request");
        return new DebugResponse(0, id + "*-*-*" + data);
    }
}

Then write the server startup program

	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();
    }

3, thrift client connection pool

The methods on the Internet are similar. I've added some features of springboot

Introducing pom dependency

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

Objects that need to be pooled

The object to be pooled is thrift's TTransport.

Object factory

To create a factory, you only need to inherit BasePooledObjectFactory and override some functions.
Because each time you create TTransport, you need to specify parameters such as ip and port, so here you use the automatic injection function @ ConfigurationProperties(prefix = "debug.thrift") of springboot. In this way, you don't need to write the configuration in the code. You can complete the configuration by adding debug.thrift.host=XX.XX.XX.XX in the configuration file app.properties.

@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) throws Exception {
    //The function here is to ensure that when the client is shut down, the server will not report an error
        if (p.getObject().isOpen()) {
            p.getObject().close();
        }
    }
}

app.properties configuration file

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

Object pool

It inherits GenericObjectPool directly, because the object we pooled is TTransport, and we prefer to get the Client, so we add two methods of getting and recycling

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);
    }
    
    //Get
    public DebugService.Client getClient() throws Exception {
        TTransport tTransport = this.borrowObject();
        TProtocol tProtocol = new TBinaryProtocol(tTransport);
        return new DebugService.Client(tProtocol);
    }

	//recovery
    public void releaseConnection(DebugService.Client client) {
        TTransport tTransport = client.getInputProtocol().getTransport();
//        TTransport tTransport = client.getOutputProtocol().getTransport();
        //Because when we declare it is new DebugService.Client(tProtocol), the two protocols here are the same. Choose one
        this.returnObject(tTransport);
    }

}

Configuration class of object pool

From the above construction method, we can see that in addition to passing in a factory, the object pool can also pass in a configuration class, so I inherited the GenericObjectPoolConfig object pool configuration class

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

As before, because @ ConfigurationProperties(prefix = "debug.pool") is added, configuration can be added to the configuration file app.properties. When writing the configuration file, the idea will also prompt us which properties can be configured.

Spring configuration class

To use DebugConnectionPool as an object pool in Spring, you need to add a configuration class and declare @ Bean

@Configuration
public class PoolAutoConfiguration {
    @Bean
    public DebugConnectionPool debugConnectionPool(PoolConfig poolConfig, DebugConnectionFactory debugConnectionFactory) {
    //This must be set here. Otherwise, spring boot will report an error that a bean has been registered
        poolConfig.setJmxEnabled(false);
        return new DebugConnectionPool(debugConnectionFactory, poolConfig);
    }
}

Four, use

At this point, all the preparations are completed. To use it, just inject 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);
	}

Five, summary

In fact, many people have written a tutorial about thrift client connection pool, but I just want to use the spring boot auto injection configuration feature in my mind, so I spent the whole afternoon tossing about, and the final result is satisfied though the function is simple.

I seldom write blog myself. Please point out the wrong place.

Published an original article, won praise 1, visited 1278
Private letter follow

Posted by vickyjackson on Thu, 05 Mar 2020 21:58:27 -0800