自定义Thrift连接池
Thrift作为一个高性能的RPC框架,广泛应用于后端服务的进程间接口调用场景中。对于Java开发者而言,thrift客户端调用框架并没有提供相关的连接池能力进行使用,对于性能要求较高的用户可以自定义Thrift连接池以满足自身的需求,本着不重复造轮子的原则,笔者使用apache commons-pools2实现了一套自定义的Thrift连接池供大家参考。
关于apache commons-pools2的相关知识,大家可以参考这篇博文:http://blog.csdn.net/amon1991/article/details/77110657
1、创建池对象工厂
PooledObjectFactory(池对象工厂)是commons-pools2中的一个概念,用于控制整个池对象的整个生命周期。
在我们的实例中,这个池对象就是TProtocol对象,准确来说,commons-pools2管理的对象其实是TProtocol在池中的封装对象,也就是实例中通过wrap(TProtocol protocol)方法包裹后生成的对象PooledObject,这个封装的对象保有实际被包装对象的复杂的状态信息,这个状态信息在判断对象是否存活,是否可以被销毁时起到了关键作用。下面给出实际的新建Thfift池对象工厂代码,笔者使用的是TMultiplexedProtocol对象,使用其他TProtocol对象的参考这种写法即可。
代码块
public class TProtocolFactory extends BasePooledObjectFactory<TProtocol>{
private final Logger logger = LoggerFactory.getLogger(this.getClass());
private static final int TIMEOUT = 3000000; // 超时时间
private static final int MAX_LENGTH = 1638400000; // 单次最大传输的数据量(1.6G)
public static final String PI_SERVICE = "PiService";
public static final String OPC_SERVICE = "OpcService";
private String host;
private int port;
private boolean keepAlive = true; // 是否持有Thrift连接,不持有则钝化时释放连接
private String sourceType; // PI数据源还是OPC数据源
public TProtocolFactory(String host, int port, boolean keepAlive, String sourceType) {
this.host = host;
this.port = port;
this.keepAlive = keepAlive;
this.sourceType = sourceType;
}
private TProtocolFactory() {
}
/**
* 创建新对象
* @return
* @throws Exception
*/
@Override
public TProtocol create() throws Exception {
TSocket tSocket = new TSocket(host, port,TIMEOUT );
TTransport tTransport = new TFramedTransport(tSocket,MAX_LENGTH);
if (null!= tTransport && !tTransport.isOpen()){
tTransport.open();
}
TProtocol protocol = new TCompactProtocol(tTransport);
if (null != sourceType) {
if (sourceType.equals(PI_SERVICE)){
return new TMultiplexedProtocol(protocol,PI_SERVICE);
}else if (sourceType.equals(OPC_SERVICE)){
return new TMultiplexedProtocol(protocol,OPC_SERVICE);
}else {
return null;
}
}else {
return null;
}
}
/**
* 将对象放入对象池前先包装对象,好让对象池可以管理
* @param protocol
* @return
*/
@Override
public PooledObject<TProtocol> wrap(TProtocol protocol) {
return new DefaultPooledObject<>(protocol);
}
/**
* 对象钝化(即:从激活状态转入非激活状态,returnObject时触发)
* @param pooledObject
* @throws TTransportException
*/
@Override
public void passivateObject(PooledObject<TProtocol> pooledObject) throws TTransportException {
if (null!= pooledObject.getObject()){
if (!keepAlive) {
pooledObject.getObject().getTransport().flush();
pooledObject.getObject().getTransport().close();
}
}
}
/**
* 对象激活(borrowObject时触发)
* @param pooledObject
* @throws TTransportException
*/
@Override
public void activateObject(PooledObject<TProtocol> pooledObject) throws TTransportException {
if (!pooledObject.getObject().getTransport().isOpen()) {
pooledObject.getObject().getTransport().open();
}
}
/**
* 对象销毁(clear时会触发)
* @param pooledObject
* @throws TTransportException
*/
@Override
public void destroyObject(PooledObject<TProtocol> pooledObject) throws TTransportException {
if (null!= pooledObject.getObject()){
if (pooledObject.getObject().getTransport().isOpen()) {
pooledObject.getObject().getTransport().flush();
pooledObject.getObject().getTransport().close();
}
}
pooledObject.markAbandoned();
}
/**
* 验证对象的有效性
* @param pooledObject
* @return
*/
@Override
public boolean validateObject(PooledObject<TProtocol> pooledObject) {
if (pooledObject.getObject() != null) {
if (pooledObject.getObject().getTransport().isOpen()) {
return true;
}
try {
pooledObject.getObject().getTransport().open();
return true;
} catch (TTransportException e) {
logger.error(e.getMessage());
}
}
return false;
}
}
2、创建对象池实例
Object Pool(对象池)是实际管理所有池对象的容器,主要的方法是borrowObject、returnObject,一个是从池中获取对象,另一个是使用完对象后将对象归还到池中,当然,该容器还提供了一些基本的方法用于获取当前存活的对象数,空闲的对象数等信息,这些信息可用于自定义销毁对象策略等实现。
由于笔者的代码中用到了Spring框架,所以外部代码获取连接池实例使用的是Spring容器管理的单例实例。注意,在对象池的使用上,必须保证对象池是线程安全的,否则可能在多线程环境导致对象池维护的数据产生不一致的问题。假设没有使用Spring框架,笔者推介使用一个饿汉模式的单例来维护pool对象即可。
@Component
public class OPCTProtocolPool {
@Autowired
private PiDataSourceConf piDataSource;
@Autowired
private ObjectPoolConfig objectPoolConfig;
private ObjectPool<TProtocol> pool;
/**
* 获取 thrift 连接对象池
* @return
*/
public ObjectPool<TProtocol> getPool() {
if (pool == null){
GenericObjectPoolConfig poolConfig = objectPoolConfig.getGenericObjectPoolConfig();
// 第一次加载对象池
pool = new GenericObjectPool<>(new TProtocolFactory(piDataSource.getIp(), piDataSource.getPort(), true ,TProtocolFactory.OPC_SERVICE), poolConfig);
}
return pool;
}
}
以上代码中,GenericObjectPoolConfig 对象可以设置连接池的参数,比如最大连接数、最大空闲数、超时时间等信息,具体可以参考commons-pools中的通用配置即可。
3、使用对象池使用TProtocol对象
完成上面两步之后,就可以从对象池中非常简单的获取TProtocol对象了,获取对象使用如下代码:
TProtocol protocol = protocolPool.getPool().borrowObject();
使用完对象后,必须使用同一个对象池返回使用的对象,否则这个对象就在进程中失控了:
protocolPool.getPool().returnObject(protocol);