/**
*
*/
package com.github.phantomthief.thrift.client.pool.impl;
import static java.util.concurrent.TimeUnit.MINUTES;
import static org.slf4j.LoggerFactory.getLogger;
import java.util.function.Function;
import org.apache.commons.pool2.KeyedPooledObjectFactory;
import org.apache.commons.pool2.PooledObject;
import org.apache.commons.pool2.impl.DefaultPooledObject;
import org.apache.commons.pool2.impl.GenericKeyedObjectPool;
import org.apache.commons.pool2.impl.GenericKeyedObjectPoolConfig;
import org.apache.thrift.transport.TFramedTransport;
import org.apache.thrift.transport.TSocket;
import org.apache.thrift.transport.TTransport;
import org.slf4j.Logger;
import com.github.phantomthief.thrift.client.pool.ThriftConnectionPoolProvider;
import com.github.phantomthief.thrift.client.pool.ThriftServerInfo;
/**
* <p>
* DefaultThriftConnectionPoolImpl class.
* </p>
*
* @author w.vela
* @version $Id: $Id
*/
public final class DefaultThriftConnectionPoolImpl implements ThriftConnectionPoolProvider {
private static final Logger logger = getLogger(DefaultThriftConnectionPoolImpl.class);
private static final int MIN_CONN = 1;
private static final int MAX_CONN = 1000;
private static final int TIMEOUT = (int) MINUTES.toMillis(5);
private final GenericKeyedObjectPool<ThriftServerInfo, TTransport> connections;
/**
* <p>
* Constructor for DefaultThriftConnectionPoolImpl.
* </p>
*
* @param config a
* {@link org.apache.commons.pool2.impl.GenericKeyedObjectPoolConfig}
* object.
* @param transportProvider a {@link java.util.function.Function}
* object.
*/
public DefaultThriftConnectionPoolImpl(GenericKeyedObjectPoolConfig config,
Function<ThriftServerInfo, TTransport> transportProvider) {
connections = new GenericKeyedObjectPool<>(new ThriftConnectionFactory(transportProvider),
config);
}
/**
* <p>
* Constructor for DefaultThriftConnectionPoolImpl.
* </p>
*
* @param config a
* {@link org.apache.commons.pool2.impl.GenericKeyedObjectPoolConfig}
* object.
*/
public DefaultThriftConnectionPoolImpl(GenericKeyedObjectPoolConfig config) {
this(config, info -> {
TSocket tsocket = new TSocket(info.getHost(), info.getPort());
tsocket.setTimeout(TIMEOUT);
return new TFramedTransport(tsocket);
});
}
/**
* <p>
* getInstance.
* </p>
*
* @return a
* {@link com.github.phantomthief.thrift.client.pool.impl.DefaultThriftConnectionPoolImpl}
* object.
*/
public static DefaultThriftConnectionPoolImpl getInstance() {
return LazyHolder.INSTANCE;
}
/** {@inheritDoc} */
@Override
public TTransport getConnection(ThriftServerInfo thriftServerInfo) {
try {
return connections.borrowObject(thriftServerInfo);
} catch (Exception e) {
logger.error("fail to get connection for {}", thriftServerInfo, e);
throw new RuntimeException(e);
}
}
/** {@inheritDoc} */
@Override
public void returnConnection(ThriftServerInfo thriftServerInfo, TTransport transport) {
connections.returnObject(thriftServerInfo, transport);
}
/** {@inheritDoc} */
@Override
public void returnBrokenConnection(ThriftServerInfo thriftServerInfo, TTransport transport) {
try {
connections.invalidateObject(thriftServerInfo, transport);
} catch (Exception e) {
logger.error("fail to invalid object:{},{}", thriftServerInfo, transport, e);
}
}
private static class LazyHolder {
private static final DefaultThriftConnectionPoolImpl INSTANCE;
static {
GenericKeyedObjectPoolConfig config = new GenericKeyedObjectPoolConfig();
config.setMaxTotal(MAX_CONN);
config.setMaxTotalPerKey(MAX_CONN);
config.setMaxIdlePerKey(MAX_CONN);
config.setMinIdlePerKey(MIN_CONN);
config.setTestOnBorrow(true);
config.setMinEvictableIdleTimeMillis(MINUTES.toMillis(1));
config.setSoftMinEvictableIdleTimeMillis(MINUTES.toMillis(1));
config.setJmxEnabled(false);
INSTANCE = new DefaultThriftConnectionPoolImpl(config);
}
}
public static final class ThriftConnectionFactory implements
KeyedPooledObjectFactory<ThriftServerInfo, TTransport> {
private final Function<ThriftServerInfo, TTransport> transportProvider;
public ThriftConnectionFactory(Function<ThriftServerInfo, TTransport> transportProvider) {
this.transportProvider = transportProvider;
}
/* (non-Javadoc)
* @see org.apache.commons.pool2.PooledObjectFactory#makeObject()
*/
@Override
public PooledObject<TTransport> makeObject(ThriftServerInfo info) throws Exception {
TTransport transport = transportProvider.apply(info);
transport.open();
DefaultPooledObject<TTransport> result = new DefaultPooledObject<>(transport);
logger.trace("make new thrift connection:{}", info);
return result;
}
/* (non-Javadoc)
* @see org.apache.commons.pool2.PooledObjectFactory#destroyObject(org.apache.commons.pool2.PooledObject)
*/
@Override
public void destroyObject(ThriftServerInfo info, PooledObject<TTransport> p)
throws Exception {
TTransport transport = p.getObject();
if (transport != null && transport.isOpen()) {
transport.close();
logger.trace("close thrift connection:{}", info);
}
}
/* (non-Javadoc)
* @see org.apache.commons.pool2.PooledObjectFactory#validateObject(org.apache.commons.pool2.PooledObject)
*/
@Override
public boolean validateObject(ThriftServerInfo info, PooledObject<TTransport> p) {
try {
return p.getObject().isOpen();
} catch (Throwable e) {
logger.error("fail to validate tsocket:{}", info, e);
return false;
}
}
/* (non-Javadoc)
* @see org.apache.commons.pool2.PooledObjectFactory#activateObject(org.apache.commons.pool2.PooledObject)
*/
@Override
public void activateObject(ThriftServerInfo info, PooledObject<TTransport> p)
throws Exception {
// do nothing
}
/* (non-Javadoc)
* @see org.apache.commons.pool2.PooledObjectFactory#passivateObject(org.apache.commons.pool2.PooledObject)
*/
@Override
public void passivateObject(ThriftServerInfo info, PooledObject<TTransport> p)
throws Exception {
// do nothing
}
}
}