All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.wealoha.thrift.ThriftClientPool Maven / Gradle / Ivy

package com.wealoha.thrift;

import java.lang.reflect.Proxy;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Function;
import java.util.stream.Collectors;

import org.apache.commons.lang3.RandomUtils;
import org.apache.commons.pool2.BasePooledObjectFactory;
import org.apache.commons.pool2.PooledObject;
import org.apache.commons.pool2.impl.DefaultPooledObject;
import org.apache.commons.pool2.impl.GenericObjectPool;
import org.apache.thrift.TServiceClient;
import org.apache.thrift.transport.TSocket;
import org.apache.thrift.transport.TTransport;
import org.apache.thrift.transport.TTransportException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.wealoha.thrift.exception.ConnectionFailException;
import com.wealoha.thrift.exception.NoBackendServiceException;
import com.wealoha.thrift.exception.ThriftException;

/**
 * Pool for ThriftClient 
*

* * ThriftClientPool pool = new ThriftClientPool(services, clientFactory) * * * @author javamonk * @createTime 2014年7月4日 下午3:55:16 */ public class ThriftClientPool { private Logger logger = LoggerFactory.getLogger(getClass()); private final Function clientFactory; private final GenericObjectPool> pool; private List services; private boolean serviceReset = false; private final PoolConfig poolConfig; /** * Construct a new pool using default config * * @param services * @param factory */ public ThriftClientPool(List services, Function factory) { this(services, factory, new PoolConfig(), null); } /** * Construct a new pool using * * @param services * @param factory All IFace(subclass of TServiceClient) were generated * by thrift. We don't know their types. Since they all extends * super class TServiceClient, * construct a new Client need only just one line: * transport->new Client(new TBinaryProtocol(transport)) * @param config */ public ThriftClientPool(List services, Function factory, PoolConfig config) { this(services, factory, config, null); } public ThriftClientPool(List services, Function factory, PoolConfig config, ThriftProtocolFactory pFactory) { if (services == null || services.size() == 0) { throw new IllegalArgumentException("services is empty!"); } if (factory == null) { throw new IllegalArgumentException("factory is empty!"); } if (config == null) { throw new IllegalArgumentException("config is empty!"); } this.services = services; this.clientFactory = factory; this.poolConfig = config; // test if config change this.poolConfig.setTestOnReturn(true); this.poolConfig.setTestOnBorrow(true); this.pool = new GenericObjectPool<>(new BasePooledObjectFactory>() { @Override public ThriftClient create() throws Exception { // get from global list first List serviceList = ThriftClientPool.this.services; ServiceInfo serviceInfo = getRandomService(serviceList); TTransport transport = getTransport(serviceInfo); try { transport.open(); } catch (TTransportException e) { logger.info("transport open fail service: host={}, port={}", serviceInfo.getHost(), serviceInfo.getPort()); if (poolConfig.isFailover()) { while (true) { try { // mark current fail and try next, until none service available serviceList = removeFailService(serviceList, serviceInfo); serviceInfo = getRandomService(serviceList); transport = getTransport(serviceInfo); // while break here logger.info("failover to next service host={}, port={}", serviceInfo.getHost(), serviceInfo.getPort()); transport.open(); break; } catch (TTransportException e2) { logger.warn("failover fail, services left: {}", serviceList.size()); } } } else { throw new ConnectionFailException("host=" + serviceInfo.getHost() + ", ip=" + serviceInfo.getPort(), e); } } ThriftClient client = new ThriftClient<>(clientFactory.apply(transport), pool, serviceInfo); logger.debug("create new object for pool {}", client); return client; } @Override public PooledObject> wrap(ThriftClient obj) { return new DefaultPooledObject<>(obj); } @Override public boolean validateObject(PooledObject> p) { ThriftClient client = p.getObject(); // check if return client in current service list if if (serviceReset) { if (!ThriftClientPool.this.services.contains(client.getServiceInfo())) { logger.warn("not return object because it's from previous config {}", client); client.closeClient(); return false; } } return super.validateObject(p); } @Override public void destroyObject(PooledObject> p) throws Exception { p.getObject().closeClient(); super.destroyObject(p); } }, poolConfig); } public List getServices() { return services; } /** * set new services for this pool * * @param services */ public void setServices(List services) { if (services == null || services.size() == 0) { throw new IllegalArgumentException("services is empty!"); } this.services = services; serviceReset = true; } private TTransport getTransport(ServiceInfo serviceInfo) { if (serviceInfo == null) { throw new NoBackendServiceException(); } TTransport transport; if (poolConfig.getTimeout() > 0) { transport = new TSocket(serviceInfo.getHost(), serviceInfo.getPort(), poolConfig.getTimeout()); } else { transport = new TSocket(serviceInfo.getHost(), serviceInfo.getPort()); } return transport; } /** * get a random service * * @param serviceList * @return */ private ServiceInfo getRandomService(List serviceList) { if (serviceList == null || serviceList.size() == 0) { return null; } return serviceList.get(RandomUtils.nextInt(0, serviceList.size())); } private List removeFailService(List list, ServiceInfo serviceInfo) { logger.info("remove service from current service list: host={}, port={}", serviceInfo.getHost(), serviceInfo.getPort()); return list.stream() // .filter(si -> !serviceInfo.equals(si)) // .collect(Collectors.toList()); } /** * get a client from pool * * @return * @throws ThriftException * @throws NoBackendServiceException if * {@link PoolConfig#setFailover(boolean)} is set and no * service can connect to * @throws ConnectionFailException if * {@link PoolConfig#setFailover(boolean)} not set and * connection fail */ public ThriftClient getClient() throws ThriftException { try { return pool.borrowObject(); } catch (Exception e) { if (e instanceof ThriftException) { throw (ThriftException) e; } throw new ThriftException("Get client from pool failed.", e); } } /** * get a client's IFace from pool *

*

    *
  • * Important: Iface is totally generated by * thrift, a ClassCastException will be thrown if assign not * match!
  • *
  • * Limitation: The return object can only used * once.
  • *
* * @return * @throws ThriftException * @throws NoBackendServiceException if * {@link PoolConfig#setFailover(boolean)} is set and no * service can connect to * @throws ConnectionFailException if * {@link PoolConfig#setFailover(boolean)} not set and * connection fail * @throws IllegalStateException if call method on return object twice */ @SuppressWarnings("unchecked") public X iface() { ThriftClient client; try { client = pool.borrowObject(); } catch (Exception e) { if (e instanceof ThriftException) { throw (ThriftException) e; } throw new ThriftException("Get client from pool failed.", e); } AtomicBoolean returnToPool = new AtomicBoolean(false); return (X) Proxy.newProxyInstance(this.getClass().getClassLoader(), client.iFace() .getClass().getInterfaces(), (proxy, method, args) -> { if (returnToPool.get()) { throw new IllegalStateException("Object returned via iface can only used once!"); } boolean success = false; try { Object result = method.invoke(client.iFace(), args); success = true; return result; } finally { if (success) { pool.returnObject(client); } else { client.closeClient(); pool.invalidateObject(client); } returnToPool.set(true); } }); } @Override protected void finalize() throws Throwable { if (pool != null) { pool.close(); } super.finalize(); } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy