package com.ganji.as.thrift.protocol.client.socket.async.pool;
import java.net.InetSocketAddress;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.atomic.AtomicBoolean;
import org.apache.commons.pool2.ObjectPool;
import org.apache.commons.pool2.PooledObjectFactory;
import org.apache.commons.pool2.impl.AbandonedConfig;
import org.apache.commons.pool2.impl.BaseObjectPoolConfig;
import org.apache.commons.pool2.impl.GenericObjectPool;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import org.slf4j.Logger;
import com.ganji.as.thrift.protocol.builder.ClientBuildingConfig;
import com.ganji.as.thrift.protocol.client.request.ThriftClientInvocation;
import com.ganji.as.thrift.protocol.client.socket.async.pool.SimpleSocketConnectionPoolProviderImpl.ConnectionMode;
import com.ganji.as.thrift.protocol.cluster.load.balance.LoadBalance;
import com.ganji.as.thrift.protocol.server.nodes.discovery.ServerNode;
import com.ganji.as.thrift.protocol.server.nodes.discovery.ServerNodesDiscovery;
import com.ganji.as.thrift.protocol.server.nodes.discovery.ServerNodesDiscoveryProvider;
/**
* @author yikangfeng
* @date 2015年8月27日
*/
class GracefulSocketConnectionPoolProviderImpl extends
AbstractSocketConnectionPool implements AutoCloseable {
final private ConcurrentMap<String, ObjectPool<SocketConnection>> transportPool_ = new ConcurrentHashMap<>();
final private Logger LOGGER_;
final private ClientBuildingConfig clientBuildingConfig_;
final private AtomicBoolean isReady_ = new AtomicBoolean(false);
final int tcpConnectionTimeout_;
final private ServerNodesDiscovery serverNodesDiscovery_;
private ConnectionMode connectionMode_;
public GracefulSocketConnectionPoolProviderImpl(
final ClientBuildingConfig clientBuildingConfig) throws Throwable {
this.LOGGER_ = clientBuildingConfig.getLogger();
this.clientBuildingConfig_ = clientBuildingConfig;
this.serverNodesDiscovery_ = ServerNodesDiscoveryProvider.FACTORY
.createServerNodesDiscovery(this.clientBuildingConfig_);
this.serverNodesDiscovery_.nodesDiscovery(this.clientBuildingConfig_
.getDestAddr());
this.tcpConnectionTimeout_ = clientBuildingConfig
.getTcpConnectTimeout();
prepare();
}
private void prepare() throws Throwable {
if (!(this.clientBuildingConfig_.getHosts() == null))
this.connectionMode_ = ConnectionMode.HOST;
if (!(this.clientBuildingConfig_.getDestAddr() == null || this.clientBuildingConfig_
.getDestAddr().isEmpty()))
this.connectionMode_ = ConnectionMode.CLUSTER;
if (this.connectionMode_ == null)
throw new IllegalStateException("Hosts or dest needs at least one.");
final InetSocketAddress hostAddress = (InetSocketAddress) this.clientBuildingConfig_
.getHosts();
final int tcpConnectionTimeout = this.clientBuildingConfig_
.getTcpConnectTimeout();
if (this.connectionMode_ == ConnectionMode.HOST)
prepareHostModeTransportPool(hostAddress, tcpConnectionTimeout);
else if (this.connectionMode_ == ConnectionMode.CLUSTER)
prepareClusterModeTransportPool(tcpConnectionTimeout);
isReady_.getAndSet(true);
if (this.LOGGER_ != null) {
if (this.LOGGER_.isInfoEnabled())
this.LOGGER_
.info("The socket connection pool prepare complete.");
}
}
private void prepareHostModeTransportPool(
final InetSocketAddress hostAddress, final int tcpConnectionTimeout)
throws Exception {
final String key = String.format("%s:%s", hostAddress.getHostName(),
hostAddress.getPort());
ObjectPool<SocketConnection> transportPool = transportPool_.get(key);
if (transportPool == null)
transportPool = createSocketConnectionPool(
hostAddress.getHostName(), hostAddress.getPort(),
this.clientBuildingConfig_.getTcpConnectTimeout());
prepareTransportPool(transportPool,
this.clientBuildingConfig_.getHostConnectionCoreSize());
transportPool_.putIfAbsent(key, transportPool);
}
private void prepareClusterModeTransportPool(final int tcpConnectionTimeout)
throws Exception {
final List<ServerNode> serverNodes = serverNodesDiscovery_.nodes();
if (serverNodes == null || serverNodes.isEmpty()) {
for (final ServerNode serverNode_ : serverNodes) {
if (serverNode_ == null)
continue;
final String key = String.format("%s:%s",
serverNode_.getHost(), serverNode_.getPort());
ObjectPool<SocketConnection> transportPool = transportPool_
.get(key);
if (transportPool == null)
transportPool = createSocketConnectionPool(
serverNode_.getHost(), serverNode_.getPort(),
this.clientBuildingConfig_.getTcpConnectTimeout());
prepareTransportPool(transportPool,
this.clientBuildingConfig_.getHostConnectionCoreSize());
transportPool_.putIfAbsent(key, transportPool);
}
}
}
static private void prepareTransportPool(
final ObjectPool<SocketConnection> transportPool,
int transportPoolInitialSocketConnectionSize) throws Exception {
if (transportPool == null)
return;
if (transportPoolInitialSocketConnectionSize <= 0)
((GenericObjectPool<SocketConnection>) transportPool).preparePool();
else {
do {
transportPool.addObject();
} while ((--transportPoolInitialSocketConnectionSize) == 0);// false
// exit.
}
}
private ObjectPool<SocketConnection> createSocketConnectionPool(
final String hostName, final int port,
final int tcpConnectionTimeout) {
return new GenericObjectPool<SocketConnection>(
getPooledSocketConnectionFactory(hostName, port,
tcpConnectionTimeout),
(GenericObjectPoolConfig) createSocketConnectionPoolConfig(),
createSocketConnectionAbandonedConfig());
}
private PooledObjectFactory<SocketConnection> getPooledSocketConnectionFactory(
final String hostName, final int port,
final int tcpConnectionTimeout) {
return PooledSocketConnectionFactory.factory(hostName, port,
tcpConnectionTimeout);
}
private BaseObjectPoolConfig createSocketConnectionPoolConfig() {
final GenericObjectPoolConfig socketConnectionPoolConfig = new GenericObjectPoolConfig();
socketConnectionPoolConfig.setMaxTotal(this.clientBuildingConfig_
.getHostConnectionLimit());
if (this.clientBuildingConfig_.getHostConnectionCoreSize() > 0)
socketConnectionPoolConfig.setMinIdle(this.clientBuildingConfig_
.getHostConnectionCoreSize());
if (this.clientBuildingConfig_.getHostConnectionMaxIdle() > 0)
socketConnectionPoolConfig.setMaxIdle(this.clientBuildingConfig_
.getHostConnectionMaxIdle());
socketConnectionPoolConfig.setMaxWaitMillis(this.clientBuildingConfig_
.getMaxWaitHostConnectionMillis());
socketConnectionPoolConfig.setNumTestsPerEvictionRun(Integer.MAX_VALUE);
socketConnectionPoolConfig.setMinEvictableIdleTimeMillis(this.clientBuildingConfig_
.getHostConnectionMinIdle());
socketConnectionPoolConfig.setTimeBetweenEvictionRunsMillis(1 * 60000L);
return socketConnectionPoolConfig;
}
private AbandonedConfig createSocketConnectionAbandonedConfig() {
final AbandonedConfig socketConnectionAbandonedConfig = new AbandonedConfig();
socketConnectionAbandonedConfig.setRemoveAbandonedOnBorrow(true);
socketConnectionAbandonedConfig.setRemoveAbandonedOnMaintenance(true);
if (this.clientBuildingConfig_.getHostConnectionMaxLifeTime() > 0)
socketConnectionAbandonedConfig
.setRemoveAbandonedTimeout(this.clientBuildingConfig_
.getHostConnectionMaxLifeTime());
return socketConnectionAbandonedConfig;
}
@Override
public boolean isReady() {
// TODO Auto-generated method stub
return isReady_.get();
}
@Override
public void removeSocketConnection(final SocketConnection socketConnection) {
// TODO Auto-generated method stub
if (socketConnection == null)
return;
final String hostName = socketConnection.getHostName();
final int port = socketConnection.getPort();
final ObjectPool<SocketConnection> transportPool = this.transportPool_
.get(String.format("%s:%s", hostName, port));
if (transportPool == null)
return;
try {
transportPool.invalidateObject(socketConnection);
} catch (Exception e) {
// TODO Auto-generated catch block
if (this.LOGGER_ != null) {
if (this.LOGGER_.isInfoEnabled())
this.LOGGER_.info(
"Error while removing socket connection,reasons.",
e);
}
}
}
@Override
public void destory() {
// TODO Auto-generated method stub
final Collection<ObjectPool<SocketConnection>> values = this.transportPool_
.values();
if (values != null && !values.isEmpty()) {
for (final ObjectPool<SocketConnection> transportPool : values) {
if (transportPool == null)
continue;
transportPool.close();
}
}
this.transportPool_.clear();
if (this.serverNodesDiscovery_ != null) {
try {
this.serverNodesDiscovery_.close();
} catch (final Exception ignored) {
}
}
}
@Override
protected SocketConnection getInternalSocketConnection(
final LoadBalance loadBalance,
final ThriftClientInvocation clientInvocation) throws Throwable {
// TODO Auto-generated method stub
SocketConnection socketConnection = null;
if (this.connectionMode_ == ConnectionMode.HOST) {
final InetSocketAddress hostAddress = (InetSocketAddress) this.clientBuildingConfig_
.getHosts();
socketConnection = getSocketConnectionByHostAndPort(
hostAddress.getHostName(), hostAddress.getPort());
} else if (this.connectionMode_ == ConnectionMode.CLUSTER) {
final List<ServerNode> serverNodes = serverNodesDiscovery_.nodes();
final ServerNode selectedServerNode = loadBalance.select(
serverNodes, clientInvocation);
if (this.LOGGER_ != null) {
if (this.LOGGER_.isInfoEnabled())
this.LOGGER_.info(String.format(
"Load balancing strategy selected node is {%s}",
selectedServerNode));
}
if (selectedServerNode == null)
return socketConnection;
socketConnection = getSocketConnectionByHostAndPort(
selectedServerNode.getHost(), selectedServerNode.getPort());
}
return socketConnection;
}
@Override
protected SocketConnection getInternalSocketConnectionByHostAndPort(
final String host, final int port) throws Throwable {
// TODO Auto-generated method stub
if (host == null || host.isEmpty() || port <= 0)
throw new IllegalArgumentException(
"The parameter host or port is not valid.");
final SocketConnection availableSocketConnection = internalAcquireSocketConnection(
host, port);
if (availableSocketConnection == null)
throw new NullPointerException(
"No idle socket connection is obtained.");
return availableSocketConnection;
}
private SocketConnection internalAcquireSocketConnection(final String host,
final int port) throws Exception {
final String key = String.format("%s:%s", host, port);
final int tcpConnectionTimeout = this.clientBuildingConfig_
.getTcpConnectTimeout();
ObjectPool<SocketConnection> transportPool = this.transportPool_
.get(key);
if (transportPool == null) {
this.transportPool_.putIfAbsent(
key,
transportPool = createSocketConnectionPool(host, port,
tcpConnectionTimeout));
prepareTransportPool(transportPool,
this.clientBuildingConfig_.getHostConnectionCoreSize());
}
try {
return transportPool.borrowObject();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
if (this.LOGGER_ != null) {
if (this.LOGGER_.isInfoEnabled())
this.LOGGER_
.info("Error while acquire socket connection from pool,reasons:",
e);
}
return null;
}
}
@Override
public void close() throws Exception {
// TODO Auto-generated method stub
this.destory();
}
@Override
public void returnSocketConnection(final SocketConnection socketConnection) {
// TODO Auto-generated method stub
if (socketConnection == null)
return;
final SocketConnection socketConnectionProxy = socketConnection;
final String key = String.format("%s:%s",
socketConnectionProxy.getHostName(),
socketConnectionProxy.getPort());
final ObjectPool<SocketConnection> transportPool = this.transportPool_
.get(key);
if (transportPool == null)
return;
try {
transportPool.returnObject(socketConnection);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
if (this.LOGGER_ != null) {
if (this.LOGGER_.isInfoEnabled())
this.LOGGER_
.info("Error while return socket connection to pool,reasons:",
e);
}
}
}
}