@Setter
@Slf4j
public class TrpcClientProxy {
    private ClientArgs clientArgs;

    @SuppressWarnings("unchecked")
    public <T> T client() {
        Class clazz;
        String className = clientArgs.getServiceInterface() + (clientArgs.isAsync() ? "$AsyncClient" : "$Client");
        try {
            clazz = Class.forName(className);
        } catch (ClassNotFoundException e) {
            log.error("class not found: " + className);
            throw new TRpcException(e);
        }

        ProxyFactory factory = new ProxyFactory();
        factory.setSuperclass(clazz);

        try {
            T t;
            if (clientArgs.isAsync()) {
                t = (T) factory.create(new Class[]{TProtocolFactory.class, TAsyncClientManager.class, TNonblockingTransport.class}, new Object[]{null, null, null});
            } else {
                t = (T) factory.create(new Class[]{TProtocol.class}, new Object[]{null});
            }
            if (this.clientArgs.isAsync()) {
                ((Proxy) t).setHandler(asyncHandler(clazz));
            } else {
                ((Proxy) t).setHandler(blockHandler(clazz));
            }
            return t;
        } catch (Exception e) {
            log.error("proxy error", e);
        }
        return null;
    }

    @SuppressWarnings("unchecked")
    private MethodHandler blockHandler(final Class clazz) {
        return (o, method, method1, args) -> {
            Pair<ThriftServerInfo, AbstractTrpcClient> borrowClient = ClientCluster.getBlockClient(clientArgs);
            Object client = borrowClient.getValue().getClient(clazz);
            boolean success = false;
            long startTime = System.currentTimeMillis();
            try {
                Object result = method.invoke(client, args);
                success = true;
                //将连接归还对象池
                clientArgs.getPoolProvider().returnConnection(borrowClient.getKey(), borrowClient.getValue());
                return result;
            } catch (Exception e) {
                log.error("call rpc error", e);
                clientArgs.getPoolProvider().returnBrokenConnection(borrowClient.getKey(), borrowClient.getValue());
                throw e;
            } finally {
                log.info("call " + method.getName() + " " + success + " time: " + (System.currentTimeMillis() - startTime));
            }
        };
    }

    @SuppressWarnings("unchecked")
    private MethodHandler asyncHandler(final Class clazz) {
        return (o, method, method1, args) -> {
            Pair<ThriftServerInfo, AbstractTrpcClient> borrowClient = ClientCluster.getBlockClient(clientArgs);
            Object client = borrowClient.getValue().getClient(clazz);
            try {
                if (args.length > 0 && args[args.length - 1] instanceof AsyncMethodCallback) {
                    //代理AsyncMethodCallback
                    args[args.length - 1] = java.lang.reflect.Proxy.newProxyInstance(AsyncMethodCallback.class.getClassLoader(),
                            new Class[]{AsyncMethodCallback.class},
                            new AsyncMethodCallbackProxy(args[args.length - 1], clientArgs, borrowClient));
                }
                return method.invoke(client, args);
            } catch (Exception e) {
                log.error("call rpc error", e);
                throw e;
            }
        };
    }

    @SuppressWarnings("unchecked")
    private class AsyncMethodCallbackProxy implements InvocationHandler {
        private Object proxied;
        private long startTime = System.currentTimeMillis();
        private ClientArgs clientArgs;
        private Pair<ThriftServerInfo, AbstractTrpcClient> borrowClient;

        private AsyncMethodCallbackProxy(Object proxied, ClientArgs clientArgs, Pair<ThriftServerInfo, AbstractTrpcClient> borrowClient) {
            this.proxied = proxied;
            this.clientArgs = clientArgs;
            this.borrowClient = borrowClient;
        }

        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            Object result;
            try {
                result = method.invoke(proxied, args);
                this.clientArgs.getPoolProvider().returnConnection(this.borrowClient.getKey(), this.borrowClient.getValue());
            } catch (Exception e) {
                this.clientArgs.getPoolProvider().returnBrokenConnection(this.borrowClient.getKey(), this.borrowClient.getValue());
                log.error("async rpc error", e);
                throw e;
            }
            log.info("call " + method.getName() + " time: " + (System.currentTimeMillis() - startTime));
            return result;
        }
    }
} @Getter
public class ClientArgs {
    private volatile TrpcClientPoolProvider poolProvider;
    private volatile AbstractLoadBalance loadBalance;
    private GenericKeyedObjectPoolConfig poolConfig;
    private List<AbstractMonitor> monitors;
    private List<String> serviceInterfaces;
    private String serviceInterface;
    private String localAppKey;
    private String remoteAppKey;
    private String ipPorts;
    private int timeout;
    private boolean async;

    @Builder
    public ClientArgs(GenericKeyedObjectPoolConfig poolConfig, List<AbstractMonitor> monitors, List<String> serviceInterfaces, String serviceInterface, String localAppKey, String remoteAppKey, String ipPorts, int timeout, boolean async) {
        this.poolConfig = poolConfig;
        this.monitors = monitors;
        this.serviceInterfaces = serviceInterfaces;
        this.serviceInterface = serviceInterface;
        this.localAppKey = localAppKey;
        this.remoteAppKey = remoteAppKey;
        this.ipPorts = ipPorts;
        this.timeout = timeout;
        this.async = async;
        afterPropertiesSet();
    }

    private void afterPropertiesSet() {
        if (StringUtils.isNoneEmpty(serviceInterface)) {
            if (serviceInterfaces == null) {
                serviceInterfaces = new ArrayList<>();
            }
            serviceInterfaces.add(serviceInterface);
        }
        if (StringUtils.isNotEmpty(ipPorts)) {
            List<ThriftServerInfo> serverInfos = Arrays.stream(ipPorts.split(",")).map(ThriftServerInfo::new).collect(Collectors.toList());
            if (monitors == null) {
                monitors = new ArrayList<>();
            }
            monitors.add(new StaticMonitor(serverInfos, serviceInterfaces, remoteAppKey));
        }
    }

    public AbstractLoadBalance getLoadBalance() {
        if (this.loadBalance == null) {
            synchronized (this) {
                if (this.loadBalance == null) {
                    this.loadBalance = new RandomLoadBalance();
                }
            }
        }
        return this.loadBalance;
    }

    public TrpcClientPoolProvider getPoolProvider() {
        if (this.poolProvider == null) {
            synchronized (this) {
                if (this.poolProvider == null) {
                    this.poolProvider = new TrpcClientPoolImpl(this.poolConfig, this.async);
                }
            }
        }
        return this.poolProvider;
    }

} public class ClassNameUtils {

    public static String getClassName(Class clazz) {
        return clazz.getName();
    }

    public static String getOuterClassName(Class clazz) {
        int spliterIndex = clazz.getName().lastIndexOf("$");
        return spliterIndex > 0 ? clazz.getName().substring(0, spliterIndex) : clazz.getName();
    }
} @SuppressWarnings({"cast", "rawtypes", "serial", "unchecked"})
@Generated(value = "Autogenerated by Thrift Compiler (0.9.3)", date = "2016-09-23")
public class Hello {

  public interface Iface {

    public String hi(String name) throws org.apache.thrift.TException;

  }

  public interface AsyncIface {

    public void hi(String name, org.apache.thrift.async.AsyncMethodCallback resultHandler) throws org.apache.thrift.TException;

  }

  public static class Client extends org.apache.thrift.TServiceClient implements Iface {
    public static class Factory implements org.apache.thrift.TServiceClientFactory<Client> {
      public Factory() {}
      public Client getClient(org.apache.thrift.protocol.TProtocol prot) {
        return new Client(prot);
      }
      public Client getClient(org.apache.thrift.protocol.TProtocol iprot, org.apache.thrift.protocol.TProtocol oprot) {
        return new Client(iprot, oprot);
      }
    }

    public Client(org.apache.thrift.protocol.TProtocol prot)
    {
      super(prot, prot);
    }

    public Client(org.apache.thrift.protocol.TProtocol iprot, org.apache.thrift.protocol.TProtocol oprot) {
      super(iprot, oprot);
    }

    public String hi(String name) throws org.apache.thrift.TException
    {
      send_hi(name);
      return recv_hi();
    }

    public void send_hi(String name) throws org.apache.thrift.TException
    {
      hi_args args = new hi_args();
      args.setName(name);
      sendBase("hi", args);
    }

    public String recv_hi() throws org.apache.thrift.TException
    {
      hi_result result = new hi_result();
      receiveBase(result, "hi");
      if (result.isSetSuccess()) {
        return result.success;
      }
      throw new org.apache.thrift.TApplicationException(org.apache.thrift.TApplicationException.MISSING_RESULT, "hi failed: unknown result");
    }

  }
  public static class AsyncClient extends org.apache.thrift.async.TAsyncClient implements AsyncIface {
    public static class Factory implements org.apache.thrift.async.TAsyncClientFactory<AsyncClient> {
      private org.apache.thrift.async.TAsyncClientManager clientManager;
      private org.apache.thrift.protocol.TProtocolFactory protocolFactory;
      public Factory(org.apache.thrift.async.TAsyncClientManager clientManager, org.apache.thrift.protocol.TProtocolFactory protocolFactory) {
        this.clientManager = clientManager;
        this.protocolFactory = protocolFactory;
      }
      public AsyncClient getAsyncClient(org.apache.thrift.transport.TNonblockingTransport transport) {
        return new AsyncClient(protocolFactory, clientManager, transport);
      }
    }

    public AsyncClient(org.apache.thrift.protocol.TProtocolFactory protocolFactory, org.apache.thrift.async.TAsyncClientManager clientManager, org.apache.thrift.transport.TNonblockingTransport transport) {
      super(protocolFactory, clientManager, transport);
    }

    public void hi(String name, org.apache.thrift.async.AsyncMethodCallback resultHandler) throws org.apache.thrift.TException {
      checkReady();
      hi_call method_call = new hi_call(name, resultHandler, this, ___protocolFactory, ___transport);
      this.___currentMethod = method_call;
      ___manager.call(method_call);
    }

    public static class hi_call extends org.apache.thrift.async.TAsyncMethodCall {
      private String name;
      public hi_call(String name, org.apache.thrift.async.AsyncMethodCallback resultHandler, org.apache.thrift.async.TAsyncClient client, org.apache.thrift.protocol.TProtocolFactory protocolFactory, org.apache.thrift.transport.TNonblockingTransport transport) throws org.apache.thrift.TException {
        super(client, protocolFactory, transport, resultHandler, false);
        this.name = name;
      }

      public void write_args(org.apache.thrift.protocol.TProtocol prot) throws org.apache.thrift.TException {
        prot.writeMessageBegin(new org.apache.thrift.protocol.TMessage("hi", org.apache.thrift.protocol.TMessageType.CALL, 0));
        hi_args args = new hi_args();
        args.setName(name);
        args.write(prot);
        prot.writeMessageEnd();
      }

      public String getResult() throws org.apache.thrift.TException {
        if (getState() != org.apache.thrift.async.TAsyncMethodCall.State.RESPONSE_READ) {
          throw new IllegalStateException("Method call not finished!");
        }
        org.apache.thrift.transport.TMemoryInputTransport memoryTransport = new org.apache.thrift.transport.TMemoryInputTransport(getFrameBuffer().array());
        org.apache.thrift.protocol.TProtocol prot = client.getProtocolFactory().getProtocol(memoryTransport);
        return (new Client(prot)).recv_hi();
      }
    }

  }

  public static class Processor<I extends Iface> extends org.apache.thrift.TBaseProcessor<I> implements org.apache.thrift.TProcessor {
    private static final Logger LOGGER = LoggerFactory.getLogger(Processor.class.getName());
    public Processor(I iface) {
      super(iface, getProcessMap(new HashMap<String, org.apache.thrift.ProcessFunction<I, ? extends org.apache.thrift.TBase>>()));
    }

    protected Processor(I iface, Map<String,  org.apache.thrift.ProcessFunction<I, ? extends  org.apache.thrift.TBase>> processMap) {
      super(iface, getProcessMap(processMap));
    }

    private static <I extends Iface> Map<String,  org.apache.thrift.ProcessFunction<I, ? extends  org.apache.thrift.TBase>> getProcessMap(Map<String,  org.apache.thrift.ProcessFunction<I, ? extends  org.apache.thrift.TBase>> processMap) {
      processMap.put("hi", new hi());
      return processMap;
    }

    public static class hi<I extends Iface> extends org.apache.thrift.ProcessFunction<I, hi_args> {
      public hi() {
        super("hi");
      }

      public hi_args getEmptyArgsInstance() {
        return new hi_args();
      }

      protected boolean isOneway() {
        return false;
      }

      public hi_result getResult(I iface, hi_args args) throws org.apache.thrift.TException {
        hi_result result = new hi_result();
        result.success = iface.hi(args.name);
        return result;
      }
    }

  }

  public static class AsyncProcessor<I extends AsyncIface> extends org.apache.thrift.TBaseAsyncProcessor<I> {
    private static final Logger LOGGER = LoggerFactory.getLogger(AsyncProcessor.class.getName());
    public AsyncProcessor(I iface) {
      super(iface, getProcessMap(new HashMap<String, org.apache.thrift.AsyncProcessFunction<I, ? extends org.apache.thrift.TBase, ?>>()));
    }

    protected AsyncProcessor(I iface, Map<String,  org.apache.thrift.AsyncProcessFunction<I, ? extends  org.apache.thrift.TBase, ?>> processMap) {
      super(iface, getProcessMap(processMap));
    }

    private static <I extends AsyncIface> Map<String,  org.apache.thrift.AsyncProcessFunction<I, ? extends  org.apache.thrift.TBase,?>> getProcessMap(Map<String,  org.apache.thrift.AsyncProcessFunction<I, ? extends  org.apache.thrift.TBase, ?>> processMap) {
      processMap.put("hi", new hi());
      return processMap;
    }

    public static class hi<I extends AsyncIface> extends org.apache.thrift.AsyncProcessFunction<I, hi_args, String> {
      public hi() {
        super("hi");
      }

      public hi_args getEmptyArgsInstance() {
        return new hi_args();
      }

      public AsyncMethodCallback<String> getResultHandler(final AsyncFrameBuffer fb, final int seqid) {
        final org.apache.thrift.AsyncProcessFunction fcall = this;
        return new AsyncMethodCallback<String>() { 
          public void onComplete(String o) {
            hi_result result = new hi_result();
            result.success = o;
            try {
              fcall.sendResponse(fb,result, org.apache.thrift.protocol.TMessageType.REPLY,seqid);
              return;
            } catch (Exception e) {
              LOGGER.error("Exception writing to internal frame buffer", e);
            }
            fb.close();
          }
          public void onError(Exception e) {
            byte msgType = org.apache.thrift.protocol.TMessageType.REPLY;
            org.apache.thrift.TBase msg;
            hi_result result = new hi_result();
            {
              msgType = org.apache.thrift.protocol.TMessageType.EXCEPTION;
              msg = (org.apache.thrift.TBase)new org.apache.thrift.TApplicationException(org.apache.thrift.TApplicationException.INTERNAL_ERROR, e.getMessage());
            }
            try {
              fcall.sendResponse(fb,msg,msgType,seqid);
              return;
            } catch (Exception ex) {
              LOGGER.error("Exception writing to internal frame buffer", ex);
            }
            fb.close();
          }
        };
      }

      protected boolean isOneway() {
        return false;
      }

      public void start(I iface, hi_args args, org.apache.thrift.async.AsyncMethodCallback<String> resultHandler) throws TException {
        iface.hi(args.name,resultHandler);
      }
    }

  }

  public static class hi_args implements org.apache.thrift.TBase<hi_args, hi_args._Fields>, java.io.Serializable, Cloneable, Comparable<hi_args>   {
    private static final org.apache.thrift.protocol.TStruct STRUCT_DESC = new org.apache.thrift.protocol.TStruct("hi_args");

    private static final org.apache.thrift.protocol.TField NAME_FIELD_DESC = new org.apache.thrift.protocol.TField("name", org.apache.thrift.protocol.TType.STRING, (short)1);

    private static final Map<Class<? extends IScheme>, SchemeFactory> schemes = new HashMap<Class<? extends IScheme>, SchemeFactory>();
    static {
      schemes.put(StandardScheme.class, new hi_argsStandardSchemeFactory());
      schemes.put(TupleScheme.class, new hi_argsTupleSchemeFactory());
    }

    public String name; // required

    /** The set of fields this struct contains, along with convenience methods for finding and manipulating them. */
    public enum _Fields implements org.apache.thrift.TFieldIdEnum {
      NAME((short)1, "name");

      private static final Map<String, _Fields> byName = new HashMap<String, _Fields>();

      static {
        for (_Fields field : EnumSet.allOf(_Fields.class)) {
          byName.put(field.getFieldName(), field);
        }
      }

      /**
       * Find the _Fields constant that matches fieldId, or null if its not found.
       */
      public static _Fields findByThriftId(int fieldId) {
        switch(fieldId) {
          case 1: // NAME
            return NAME;
          default:
            return null;
        }
      }

      /**
       * Find the _Fields constant that matches fieldId, throwing an exception
       * if it is not found.
       */
      public static _Fields findByThriftIdOrThrow(int fieldId) {
        _Fields fields = findByThriftId(fieldId);
        if (fields == null) throw new IllegalArgumentException("Field " + fieldId + " doesn't exist!");
        return fields;
      }

      /**
       * Find the _Fields constant that matches name, or null if its not found.
       */
      public static _Fields findByName(String name) {
        return byName.get(name);
      }

      private final short _thriftId;
      private final String _fieldName;

      _Fields(short thriftId, String fieldName) {
        _thriftId = thriftId;
        _fieldName = fieldName;
      }

      public short getThriftFieldId() {
        return _thriftId;
      }

      public String getFieldName() {
        return _fieldName;
      }
    }

    // isset id assignments
    public static final Map<_Fields, org.apache.thrift.meta_data.FieldMetaData> metaDataMap;
    static {
      Map<_Fields, org.apache.thrift.meta_data.FieldMetaData> tmpMap = new EnumMap<_Fields, org.apache.thrift.meta_data.FieldMetaData>(_Fields.class);
      tmpMap.put(_Fields.NAME, new org.apache.thrift.meta_data.FieldMetaData("name", org.apache.thrift.TFieldRequirementType.DEFAULT, 
          new org.apache.thrift.meta_data.FieldValueMetaData(org.apache.thrift.protocol.TType.STRING)));
      metaDataMap = Collections.unmodifiableMap(tmpMap);
      org.apache.thrift.meta_data.FieldMetaData.addStructMetaDataMap(hi_args.class, metaDataMap);
    }

    public hi_args() {
    }

    public hi_args(
      String name)
    {
      this();
      this.name = name;
    }

    /**
     * Performs a deep copy on <i>other</i>.
     */
    public hi_args(hi_args other) {
      if (other.isSetName()) {
        this.name = other.name;
      }
    }

    public hi_args deepCopy() {
      return new hi_args(this);
    }

    @Override
    public void clear() {
      this.name = null;
    }

    public String getName() {
      return this.name;
    }

    public hi_args setName(String name) {
      this.name = name;
      return this;
    }

    public void unsetName() {
      this.name = null;
    }

    /** Returns true if field name is set (has been assigned a value) and false otherwise */
    public boolean isSetName() {
      return this.name != null;
    }

    public void setNameIsSet(boolean value) {
      if (!value) {
        this.name = null;
      }
    }

    public void setFieldValue(_Fields field, Object value) {
      switch (field) {
      case NAME:
        if (value == null) {
          unsetName();
        } else {
          setName((String)value);
        }
        break;

      }
    }

    public Object getFieldValue(_Fields field) {
      switch (field) {
      case NAME:
        return getName();

      }
      throw new IllegalStateException();
    }

    /** Returns true if field corresponding to fieldID is set (has been assigned a value) and false otherwise */
    public boolean isSet(_Fields field) {
      if (field == null) {
        throw new IllegalArgumentException();
      }

      switch (field) {
      case NAME:
        return isSetName();
      }
      throw new IllegalStateException();
    }

    @Override
    public boolean equals(Object that) {
      if (that == null)
        return false;
      if (that instanceof hi_args)
        return this.equals((hi_args)that);
      return false;
    }

    public boolean equals(hi_args that) {
      if (that == null)
        return false;

      boolean this_present_name = true && this.isSetName();
      boolean that_present_name = true && that.isSetName();
      if (this_present_name || that_present_name) {
        if (!(this_present_name && that_present_name))
          return false;
        if (!this.name.equals(that.name))
          return false;
      }

      return true;
    }

    @Override
    public int hashCode() {
      List<Object> list = new ArrayList<Object>();

      boolean present_name = true && (isSetName());
      list.add(present_name);
      if (present_name)
        list.add(name);

      return list.hashCode();
    }

    @Override
    public int compareTo(hi_args other) {
      if (!getClass().equals(other.getClass())) {
        return getClass().getName().compareTo(other.getClass().getName());
      }

      int lastComparison = 0;

      lastComparison = Boolean.valueOf(isSetName()).compareTo(other.isSetName());
      if (lastComparison != 0) {
        return lastComparison;
      }
      if (isSetName()) {
        lastComparison = org.apache.thrift.TBaseHelper.compareTo(this.name, other.name);
        if (lastComparison != 0) {
          return lastComparison;
        }
      }
      return 0;
    }

    public _Fields fieldForId(int fieldId) {
      return _Fields.findByThriftId(fieldId);
    }

    public void read(org.apache.thrift.protocol.TProtocol iprot) throws org.apache.thrift.TException {
      schemes.get(iprot.getScheme()).getScheme().read(iprot, this);
    }

    public void write(org.apache.thrift.protocol.TProtocol oprot) throws org.apache.thrift.TException {
      schemes.get(oprot.getScheme()).getScheme().write(oprot, this);
    }

    @Override
    public String toString() {
      StringBuilder sb = new StringBuilder("hi_args(");
      boolean first = true;

      sb.append("name:");
      if (this.name == null) {
        sb.append("null");
      } else {
        sb.append(this.name);
      }
      first = false;
      sb.append(")");
      return sb.toString();
    }

    public void validate() throws org.apache.thrift.TException {
      // check for required fields
      // check for sub-struct validity
    }

    private void writeObject(java.io.ObjectOutputStream out) throws java.io.IOException {
      try {
        write(new org.apache.thrift.protocol.TCompactProtocol(new org.apache.thrift.transport.TIOStreamTransport(out)));
      } catch (org.apache.thrift.TException te) {
        throw new java.io.IOException(te);
      }
    }

    private void readObject(java.io.ObjectInputStream in) throws java.io.IOException, ClassNotFoundException {
      try {
        read(new org.apache.thrift.protocol.TCompactProtocol(new org.apache.thrift.transport.TIOStreamTransport(in)));
      } catch (org.apache.thrift.TException te) {
        throw new java.io.IOException(te);
      }
    }

    private static class hi_argsStandardSchemeFactory implements SchemeFactory {
      public hi_argsStandardScheme getScheme() {
        return new hi_argsStandardScheme();
      }
    }

    private static class hi_argsStandardScheme extends StandardScheme<hi_args> {

      public void read(org.apache.thrift.protocol.TProtocol iprot, hi_args struct) throws org.apache.thrift.TException {
        org.apache.thrift.protocol.TField schemeField;
        iprot.readStructBegin();
        while (true)
        {
          schemeField = iprot.readFieldBegin();
          if (schemeField.type == org.apache.thrift.protocol.TType.STOP) { 
            break;
          }
          switch (schemeField.id) {
            case 1: // NAME
              if (schemeField.type == org.apache.thrift.protocol.TType.STRING) {
                struct.name = iprot.readString();
                struct.setNameIsSet(true);
              } else { 
                org.apache.thrift.protocol.TProtocolUtil.skip(iprot, schemeField.type);
              }
              break;
            default:
              org.apache.thrift.protocol.TProtocolUtil.skip(iprot, schemeField.type);
          }
          iprot.readFieldEnd();
        }
        iprot.readStructEnd();

        // check for required fields of primitive type, which can't be checked in the validate method
        struct.validate();
      }

      public void write(org.apache.thrift.protocol.TProtocol oprot, hi_args struct) throws org.apache.thrift.TException {
        struct.validate();

        oprot.writeStructBegin(STRUCT_DESC);
        if (struct.name != null) {
          oprot.writeFieldBegin(NAME_FIELD_DESC);
          oprot.writeString(struct.name);
          oprot.writeFieldEnd();
        }
        oprot.writeFieldStop();
        oprot.writeStructEnd();
      }

    }

    private static class hi_argsTupleSchemeFactory implements SchemeFactory {
      public hi_argsTupleScheme getScheme() {
        return new hi_argsTupleScheme();
      }
    }

    private static class hi_argsTupleScheme extends TupleScheme<hi_args> {

      @Override
      public void write(org.apache.thrift.protocol.TProtocol prot, hi_args struct) throws org.apache.thrift.TException {
        TTupleProtocol oprot = (TTupleProtocol) prot;
        BitSet optionals = new BitSet();
        if (struct.isSetName()) {
          optionals.set(0);
        }
        oprot.writeBitSet(optionals, 1);
        if (struct.isSetName()) {
          oprot.writeString(struct.name);
        }
      }

      @Override
      public void read(org.apache.thrift.protocol.TProtocol prot, hi_args struct) throws org.apache.thrift.TException {
        TTupleProtocol iprot = (TTupleProtocol) prot;
        BitSet incoming = iprot.readBitSet(1);
        if (incoming.get(0)) {
          struct.name = iprot.readString();
          struct.setNameIsSet(true);
        }
      }
    }

  }

  public static class hi_result implements org.apache.thrift.TBase<hi_result, hi_result._Fields>, java.io.Serializable, Cloneable, Comparable<hi_result>   {
    private static final org.apache.thrift.protocol.TStruct STRUCT_DESC = new org.apache.thrift.protocol.TStruct("hi_result");

    private static final org.apache.thrift.protocol.TField SUCCESS_FIELD_DESC = new org.apache.thrift.protocol.TField("success", org.apache.thrift.protocol.TType.STRING, (short)0);

    private static final Map<Class<? extends IScheme>, SchemeFactory> schemes = new HashMap<Class<? extends IScheme>, SchemeFactory>();
    static {
      schemes.put(StandardScheme.class, new hi_resultStandardSchemeFactory());
      schemes.put(TupleScheme.class, new hi_resultTupleSchemeFactory());
    }

    public String success; // required

    /** The set of fields this struct contains, along with convenience methods for finding and manipulating them. */
    public enum _Fields implements org.apache.thrift.TFieldIdEnum {
      SUCCESS((short)0, "success");

      private static final Map<String, _Fields> byName = new HashMap<String, _Fields>();

      static {
        for (_Fields field : EnumSet.allOf(_Fields.class)) {
          byName.put(field.getFieldName(), field);
        }
      }

      /**
       * Find the _Fields constant that matches fieldId, or null if its not found.
       */
      public static _Fields findByThriftId(int fieldId) {
        switch(fieldId) {
          case 0: // SUCCESS
            return SUCCESS;
          default:
            return null;
        }
      }

      /**
       * Find the _Fields constant that matches fieldId, throwing an exception
       * if it is not found.
       */
      public static _Fields findByThriftIdOrThrow(int fieldId) {
        _Fields fields = findByThriftId(fieldId);
        if (fields == null) throw new IllegalArgumentException("Field " + fieldId + " doesn't exist!");
        return fields;
      }

      /**
       * Find the _Fields constant that matches name, or null if its not found.
       */
      public static _Fields findByName(String name) {
        return byName.get(name);
      }

      private final short _thriftId;
      private final String _fieldName;

      _Fields(short thriftId, String fieldName) {
        _thriftId = thriftId;
        _fieldName = fieldName;
      }

      public short getThriftFieldId() {
        return _thriftId;
      }

      public String getFieldName() {
        return _fieldName;
      }
    }

    // isset id assignments
    public static final Map<_Fields, org.apache.thrift.meta_data.FieldMetaData> metaDataMap;
    static {
      Map<_Fields, org.apache.thrift.meta_data.FieldMetaData> tmpMap = new EnumMap<_Fields, org.apache.thrift.meta_data.FieldMetaData>(_Fields.class);
      tmpMap.put(_Fields.SUCCESS, new org.apache.thrift.meta_data.FieldMetaData("success", org.apache.thrift.TFieldRequirementType.DEFAULT, 
          new org.apache.thrift.meta_data.FieldValueMetaData(org.apache.thrift.protocol.TType.STRING)));
      metaDataMap = Collections.unmodifiableMap(tmpMap);
      org.apache.thrift.meta_data.FieldMetaData.addStructMetaDataMap(hi_result.class, metaDataMap);
    }

    public hi_result() {
    }

    public hi_result(
      String success)
    {
      this();
      this.success = success;
    }

    /**
     * Performs a deep copy on <i>other</i>.
     */
    public hi_result(hi_result other) {
      if (other.isSetSuccess()) {
        this.success = other.success;
      }
    }

    public hi_result deepCopy() {
      return new hi_result(this);
    }

    @Override
    public void clear() {
      this.success = null;
    }

    public String getSuccess() {
      return this.success;
    }

    public hi_result setSuccess(String success) {
      this.success = success;
      return this;
    }

    public void unsetSuccess() {
      this.success = null;
    }

    /** Returns true if field success is set (has been assigned a value) and false otherwise */
    public boolean isSetSuccess() {
      return this.success != null;
    }

    public void setSuccessIsSet(boolean value) {
      if (!value) {
        this.success = null;
      }
    }

    public void setFieldValue(_Fields field, Object value) {
      switch (field) {
      case SUCCESS:
        if (value == null) {
          unsetSuccess();
        } else {
          setSuccess((String)value);
        }
        break;

      }
    }

    public Object getFieldValue(_Fields field) {
      switch (field) {
      case SUCCESS:
        return getSuccess();

      }
      throw new IllegalStateException();
    }

    /** Returns true if field corresponding to fieldID is set (has been assigned a value) and false otherwise */
    public boolean isSet(_Fields field) {
      if (field == null) {
        throw new IllegalArgumentException();
      }

      switch (field) {
      case SUCCESS:
        return isSetSuccess();
      }
      throw new IllegalStateException();
    }

    @Override
    public boolean equals(Object that) {
      if (that == null)
        return false;
      if (that instanceof hi_result)
        return this.equals((hi_result)that);
      return false;
    }

    public boolean equals(hi_result that) {
      if (that == null)
        return false;

      boolean this_present_success = true && this.isSetSuccess();
      boolean that_present_success = true && that.isSetSuccess();
      if (this_present_success || that_present_success) {
        if (!(this_present_success && that_present_success))
          return false;
        if (!this.success.equals(that.success))
          return false;
      }

      return true;
    }

    @Override
    public int hashCode() {
      List<Object> list = new ArrayList<Object>();

      boolean present_success = true && (isSetSuccess());
      list.add(present_success);
      if (present_success)
        list.add(success);

      return list.hashCode();
    }

    @Override
    public int compareTo(hi_result other) {
      if (!getClass().equals(other.getClass())) {
        return getClass().getName().compareTo(other.getClass().getName());
      }

      int lastComparison = 0;

      lastComparison = Boolean.valueOf(isSetSuccess()).compareTo(other.isSetSuccess());
      if (lastComparison != 0) {
        return lastComparison;
      }
      if (isSetSuccess()) {
        lastComparison = org.apache.thrift.TBaseHelper.compareTo(this.success, other.success);
        if (lastComparison != 0) {
          return lastComparison;
        }
      }
      return 0;
    }

    public _Fields fieldForId(int fieldId) {
      return _Fields.findByThriftId(fieldId);
    }

    public void read(org.apache.thrift.protocol.TProtocol iprot) throws org.apache.thrift.TException {
      schemes.get(iprot.getScheme()).getScheme().read(iprot, this);
    }

    public void write(org.apache.thrift.protocol.TProtocol oprot) throws org.apache.thrift.TException {
      schemes.get(oprot.getScheme()).getScheme().write(oprot, this);
      }

    @Override
    public String toString() {
      StringBuilder sb = new StringBuilder("hi_result(");
      boolean first = true;

      sb.append("success:");
      if (this.success == null) {
        sb.append("null");
      } else {
        sb.append(this.success);
      }
      first = false;
      sb.append(")");
      return sb.toString();
    }

    public void validate() throws org.apache.thrift.TException {
      // check for required fields
      // check for sub-struct validity
    }

    private void writeObject(java.io.ObjectOutputStream out) throws java.io.IOException {
      try {
        write(new org.apache.thrift.protocol.TCompactProtocol(new org.apache.thrift.transport.TIOStreamTransport(out)));
      } catch (org.apache.thrift.TException te) {
        throw new java.io.IOException(te);
      }
    }

    private void readObject(java.io.ObjectInputStream in) throws java.io.IOException, ClassNotFoundException {
      try {
        read(new org.apache.thrift.protocol.TCompactProtocol(new org.apache.thrift.transport.TIOStreamTransport(in)));
      } catch (org.apache.thrift.TException te) {
        throw new java.io.IOException(te);
      }
    }

    private static class hi_resultStandardSchemeFactory implements SchemeFactory {
      public hi_resultStandardScheme getScheme() {
        return new hi_resultStandardScheme();
      }
    }

    private static class hi_resultStandardScheme extends StandardScheme<hi_result> {

      public void read(org.apache.thrift.protocol.TProtocol iprot, hi_result struct) throws org.apache.thrift.TException {
        org.apache.thrift.protocol.TField schemeField;
        iprot.readStructBegin();
        while (true)
        {
          schemeField = iprot.readFieldBegin();
          if (schemeField.type == org.apache.thrift.protocol.TType.STOP) { 
            break;
          }
          switch (schemeField.id) {
            case 0: // SUCCESS
              if (schemeField.type == org.apache.thrift.protocol.TType.STRING) {
                struct.success = iprot.readString();
                struct.setSuccessIsSet(true);
              } else { 
                org.apache.thrift.protocol.TProtocolUtil.skip(iprot, schemeField.type);
              }
              break;
            default:
              org.apache.thrift.protocol.TProtocolUtil.skip(iprot, schemeField.type);
          }
          iprot.readFieldEnd();
        }
        iprot.readStructEnd();

        // check for required fields of primitive type, which can't be checked in the validate method
        struct.validate();
      }

      public void write(org.apache.thrift.protocol.TProtocol oprot, hi_result struct) throws org.apache.thrift.TException {
        struct.validate();

        oprot.writeStructBegin(STRUCT_DESC);
        if (struct.success != null) {
          oprot.writeFieldBegin(SUCCESS_FIELD_DESC);
          oprot.writeString(struct.success);
          oprot.writeFieldEnd();
        }
        oprot.writeFieldStop();
        oprot.writeStructEnd();
      }

    }

    private static class hi_resultTupleSchemeFactory implements SchemeFactory {
      public hi_resultTupleScheme getScheme() {
        return new hi_resultTupleScheme();
      }
    }

    private static class hi_resultTupleScheme extends TupleScheme<hi_result> {

      @Override
      public void write(org.apache.thrift.protocol.TProtocol prot, hi_result struct) throws org.apache.thrift.TException {
        TTupleProtocol oprot = (TTupleProtocol) prot;
        BitSet optionals = new BitSet();
        if (struct.isSetSuccess()) {
          optionals.set(0);
        }
        oprot.writeBitSet(optionals, 1);
        if (struct.isSetSuccess()) {
          oprot.writeString(struct.success);
        }
      }

      @Override
      public void read(org.apache.thrift.protocol.TProtocol prot, hi_result struct) throws org.apache.thrift.TException {
        TTupleProtocol iprot = (TTupleProtocol) prot;
        BitSet incoming = iprot.readBitSet(1);
        if (incoming.get(0)) {
          struct.success = iprot.readString();
          struct.setSuccessIsSet(true);
        }
      }
    }

  }

} @Slf4j
@Setter
public class ZkMonitor extends AbstractMonitor {

    private String connectString;
    private int sessionTimeout = 3000;
    private int connectionTimeout = 100;
    private int retrySleepTime = 100;
    private int maxRetries = 3;

    private CuratorFramework client = null;

    private void initMonitor() {
        client = CuratorFrameworkFactory.builder()
                .connectString(connectString)
                .sessionTimeoutMs(sessionTimeout)
                .connectionTimeoutMs(connectionTimeout)
                .canBeReadOnly(false)
                .retryPolicy(new ExponentialBackoffRetry(retrySleepTime, maxRetries))
                .namespace(ZkConstant.NAMESPACE)
                .build();
        client.start();
        client.getConnectionStateListenable().addListener((client1, newState)
                -> log.info("zookeeper client " + connectString + " change status: " + newState.name()));
        log.info("init zookeeper connect: " + connectString);
    }

    public CuratorFramework getClient() {
        if (this.client == null) {
            initMonitor();
        }
        return client;
    }

    @Override
    public List<RegistryConfig> monitorRemoteKey(RegistryConfigListener listener) {
        try {
            final PathChildrenCache childrenCache = new PathChildrenCache(getClient(), ZkConstant.SERVICES_DIR + listener.getRemoteKey(), true);
            childrenCache.getListenable().addListener(
                    (client1, event) -> {
                        switch (event.getType()) {
                            case CHILD_ADDED:
                                listener.addServer(RegistryConfig.parse(new String(event.getData().getData())));
                                log.info("CHILD_ADDED: " + event.getData().getPath());
                                break;
                            case CHILD_REMOVED:
                                listener.removeServer(nodeName2ServerInfo(event.getData().getPath()));
                                log.info("CHILD_REMOVED: " + event.getData().getPath());
                                break;
                            case CHILD_UPDATED:
                                String newData = new String(event.getData().getData());
                                listener.updateServer(RegistryConfig.parse(newData));
                                log.info("CHILD_UPDATED: " + event.getData().getPath());
                                break;
                            default:
                                break;
                        }
                    }, ZkConstant.zkPool);
            childrenCache.start(PathChildrenCache.StartMode.BUILD_INITIAL_CACHE);
            return childrenCache.getCurrentData().stream().map(d -> RegistryConfig.parse(new String(d.getData()))).collect(Collectors.toList());
        } catch (Exception e) {
            log.error("startMonitor error", e);
        }
        return null;
    }

    private ThriftServerInfo nodeName2ServerInfo(String path) {
        if (path.lastIndexOf("-") > 0) {
            return ThriftServerInfo.parse(path.substring(path.lastIndexOf("-") + 1));
        }
        return null;
    }
} @Slf4j
@Setter
public class ZkRegistry implements IRegistry {
    public static final String registry = "zookeeper";
    private static final Map<String, Map<String, PersistentNode>> nodeNames = new ConcurrentHashMap<>();
    private String connectString;
    private int sessionTimeout;
    private int connectionTimeout;
    private int retrySleepTime = 100;
    private int maxRetries = 3;

    private CuratorFramework client;

    public void initConnect() {
        client = CuratorFrameworkFactory.builder()
                .connectString(connectString)
                .sessionTimeoutMs(sessionTimeout)
                .connectionTimeoutMs(connectionTimeout)
                .canBeReadOnly(false)
                .retryPolicy(new ExponentialBackoffRetry(retrySleepTime, maxRetries))
                .namespace(ZkConstant.NAMESPACE)
                .build();
        client.start();
        log.info("init zookeeper connect: " + connectString);
    }

    @Override
    public void registry(RegistryConfig registryConfig) {
        registryConfig.setRegistry(registry);
        try {
            String nodeName = registryConfig.getServerInfo().toString();
            Map<String, PersistentNode> appKeyServers = nodeNames.computeIfAbsent(registryConfig.getAppKey(), (key) -> new ConcurrentHashMap<>());
            if (appKeyServers.get(nodeName) != null) {
                log.error("server registry exists: " + nodeName);
                throw new TRpcRegistryException("server registry exists for key：" + nodeName);
            }
            String nodeValue = registryConfig.toJsonString();
            PersistentNode node = new PersistentNode(client, CreateMode.EPHEMERAL, true, ZkConstant.SERVICES_DIR + registryConfig.getAppKey() + "/" + nodeName, nodeValue.getBytes());
            node.start();
            node.waitForInitialCreate(3, TimeUnit.SECONDS);
            appKeyServers.put(nodeName, node);
            String actualPath = node.getActualPath();
            log.info("registry to zookeeper, node: " + actualPath + " value: " + nodeValue);
        } catch (Exception e) {
            if (e instanceof RuntimeException) {
                throw (RuntimeException) e;
            } else {
                log.error("registry error", e);
            }
        }
    }

    @Override
    public void modify(RegistryConfig registryConfig) {
        registryConfig.setRegistry(registry);
        try {
            String nodeName = registryConfig.getServerInfo().toString();
            Map<String, PersistentNode> appKeyServers = nodeNames.computeIfAbsent(registryConfig.getAppKey(), (key) -> new ConcurrentHashMap<>());
            PersistentNode node = appKeyServers.get(nodeName);
            if (node == null) {
                log.warn("server registry not exists " + nodeName + " try to registry");
                registry(registryConfig);
                return;
            }
            String nodeValue = registryConfig.toJsonString();
            node.setData(nodeValue.getBytes());
            String actualPath = node.getActualPath();
            log.info("zookeeper modify, node: " + actualPath + " value: " + nodeValue);
        } catch (Exception e) {
            log.error("modify error", e);
        }
    }

    @Override
    public boolean unRegistry(String appKey, ThriftServerInfo serverInfo) {
        try {
            Map<String, PersistentNode> appKeyServers = nodeNames.computeIfAbsent(appKey, (key) -> new ConcurrentHashMap<>());
            PersistentNode node = appKeyServers.get(serverInfo.toString());
            if (node != null) {
                log.info("unRegistry " + serverInfo + " actualPath: " + node.getActualPath());
                node.close();
                appKeyServers.remove(serverInfo.toString());
                return true;
            } else {
                log.warn("unRegistry " + serverInfo + " node not found");
            }
        } catch (Exception e) {
            log.error("unRegistry error, serverInfo: " + serverInfo, e);
        }
        return false;
    }

} @Slf4j
public class ThriftServerPublisher {
    private ServerArgs serverArgs;
    private TServer server;

    public ThriftServerPublisher(ServerArgs serverArgs) {
        serverArgs.afterPropertiesSet();
        this.serverArgs = serverArgs;
    }

    public void init() {
        try {
            TMultiplexedProcessor processor = new TMultiplexedProcessor();
            for (ServiceArgs service : serverArgs.getServices()) {
                String className = service.getService();
                if (className.endsWith("$Processor")) {
                    className = className.substring(0, className.indexOf("$Processor"));
                }
                processor.registerProcessor(className, service.getProcessor());
            }
            if (serverArgs.getNettyServerArgs() != null) {
                this.server = new TNettyServer(serverArgs.getNettyServerArgs().ip(serverArgs.getHost()).port(serverArgs.getPort()));
            } else {
                TNonblockingServerSocket serverTransport = new TNonblockingServerSocket(new InetSocketAddress(serverArgs.getHost(), serverArgs.getPort()));
                //异步IO，需要使用TFramedTransport，它将分块缓存读取。
                TTransportFactory transportFactory = new TFramedTransport.Factory();
                //使用高密度二进制协议
                TProtocolFactory proFactory = new TBinaryProtocol.Factory();
                // Use this for a multithreaded key
                this.server = new TThreadedSelectorServer(new
                        TThreadedSelectorServer.Args(serverTransport)
                        .transportFactory(transportFactory)
                        .protocolFactory(proFactory)
                        .processor(processor)
                );
            }
            log.info("Starting the Thrift key...");
            this.server.setServerEventHandler(new TrpcRegistryEventHandler(serverArgs));
            this.server.serve();
            if (this.serverArgs.getNettyServerArgs() != null) {
                ((TNettyServer) this.server).waitForClose();
            }
        } catch (Exception e) {
            log.error("publish thrift key error", e);
        }
    }

    public void stop() {
        if (this.server != null && this.server.isServing()) {
            this.server.stop();
        }
    }
} @Data
@Builder
public class ServerArgs {
    @Singular
    private List<IRegistry> registrys = Collections.emptyList();
    @Size(min = 1, max = 100)
    @Singular
    private List<ServiceArgs> services = Collections.emptyList();
    private NettyServerArgs nettyServerArgs;
    @NotBlank
    private String appkey;  //localAppKey
    @NotBlank
    private String host;
    @NotBlank
    private String hostName;
    @Min(1)
    private int port;
    @Min(0)
    @Max(100)
    private int weight = 100;

    public void setWeight(int weight) {
        services.stream().filter(s -> s.getWeight() == this.weight).forEach(s -> s.setWeight(weight));
        this.weight = weight;
    }

    //对象创建完成后执行此方法
    public void afterPropertiesSet() {
        InetAddress localAddress = NetUtils.getLocalAddress();
        if (StringUtils.isBlank(host)) {
            this.host = localAddress.getHostAddress();
        }
        services.stream().filter(s -> s.getWeight() < 0).forEach(s -> s.setWeight(weight));
        this.hostName = localAddress.getHostName();
        ValidationUtils.validateWithException(this);
    }

    public RegistryConfig getRegistryConfig() {
        return RegistryConfig.builder()
                .serverInfo(new ThriftServerInfo(this.host, this.port))
                .servers(this.services.stream().map(ServiceArgs::getServiceConfig).collect(Collectors.toList()))
                .appKey(this.appkey)
                .hostName(this.hostName)
                .weight(this.weight)
                .build();
    }

} public class ServiceFactoryTest {
    private String zkconnStr = "localhost:2181";
    private String appKey = "serviceFactoryTest";

    public ServerArgs getServerArgs(String appKey, String host, int port) {
        ServerArgs arg = ServerArgs.builder()
                .appkey(appKey)
                .port(port)
                .host(host)
                .weight(80)
                .service(new ServiceArgs(new Hello.Processor<>(new HelloServer()), ClassNameUtils.getClassName(Hello.class), 80, 100))
                .build();
        arg.afterPropertiesSet();
        return arg;
    }

    public ZkRegistry getZkRegistry() {
        ZkRegistry registry = new ZkRegistry();
        registry.setConnectString(zkconnStr);
        registry.setSessionTimeout(100);
        registry.setConnectionTimeout(1000);
        registry.initConnect();
        return registry;
    }

    @Test
    public void test() throws InterruptedException {
        ZkRegistry registry = getZkRegistry();
        ServerArgs arg = getServerArgs(appKey, "127.0.0.1", 8000);
        registry.registry(arg.getRegistryConfig());
        TimeUnit.MILLISECONDS.sleep(5);

        ZkMonitor zkMonitor = new ZkMonitor();
        zkMonitor.setConnectString(zkconnStr);
        Set<TrpcServiceNode> nodes = ServiceFactory.getServiceKeys(new ServiceKey(appKey, ClassNameUtils.getClassName(Hello.class)), zkMonitor);
        assertThat(nodes.size()).isEqualTo(1);
        nodes.forEach(System.out::println);

        //test add one
        ServerArgs arg2 = getServerArgs(appKey, "127.0.0.1", 8001);
        registry.registry(arg2.getRegistryConfig());
        TimeUnit.MILLISECONDS.sleep(10);

        nodes = ServiceFactory.getServiceKeys(new ServiceKey(appKey, ClassNameUtils.getClassName(Hello.class)), zkMonitor);
        assertThat(nodes.size()).isEqualTo(2);
        nodes.forEach(System.out::println);

        //test update
        arg2.setWeight(70);
        registry.modify(arg2.getRegistryConfig());
        TimeUnit.MILLISECONDS.sleep(20);
        nodes = ServiceFactory.getServiceKeys(new ServiceKey(appKey, ClassNameUtils.getClassName(Hello.class)), zkMonitor);
        assertThat(nodes.size()).isEqualTo(2);
        nodes.forEach(System.out::println);
        assertThat(Lists.newArrayList(nodes).get(1).getWeight()).isEqualTo(70);

        //test delete
        registry.unRegistry(appKey, arg2.getRegistryConfig().getServerInfo());
        TimeUnit.MILLISECONDS.sleep(10);
        nodes = ServiceFactory.getServiceKeys(new ServiceKey(appKey, ClassNameUtils.getClassName(Hello.class)), zkMonitor);
        assertThat(nodes.size()).isEqualTo(1);
        nodes.forEach(System.out::println);
    }

} public class NumUtil {
    private static final AtomicLong NUM = new AtomicLong();
    private static final AtomicInteger PORT = new AtomicInteger(10000);

    public static long nextNum() {
        return NUM.incrementAndGet();
    }

    public static int nextPort() {
        return PORT.incrementAndGet();
    }

}
import com.github.sofn.trpc.client.TrpcClientProxy;
import com.github.sofn.trpc.client.config.ClientArgs;
import com.github.sofn.trpc.core.utils.ClassNameUtils;
import com.github.sofn.trpc.demo.Hello;
import com.github.sofn.trpc.registry.zk.ZkMonitor;
import com.github.sofn.trpc.registry.zk.ZkRegistry;
import com.github.sofn.trpc.server.ThriftServerPublisher;
import com.github.sofn.trpc.server.config.ServerArgs;
import com.github.sofn.trpc.test.monitor.ServiceFactoryTest;
import com.github.sofn.trpc.utils.NumUtil;
import com.google.common.collect.ImmutableList;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.pool2.impl.GenericKeyedObjectPoolConfig;
import org.apache.thrift.TException;
import org.apache.thrift.async.AsyncMethodCallback;
import org.junit.Before;
import org.junit.Test;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import static java.util.concurrent.TimeUnit.MINUTES;
import static org.junit.Assert.*;
package com.github.sofn.trpc.test;




/**
 * @author sofn
 * @version 1.0 Created at: 2016-10-03 22:59
 */
@Slf4j
public class TrpcClientProxyTest {
    private static final int MIN_CONN = 1;
    private static final int MAX_CONN = 1000;
    private static final String localAppKey = "clientkey";
    private String appKey;

    private GenericKeyedObjectPoolConfig poolConfig;

    @Before
    public void init() throws InterruptedException {
        this.appKey = "proxytest" + NumUtil.nextNum();
        ServiceFactoryTest serviceFactoryTest = new ServiceFactoryTest();
        ServerArgs serverArgs = serviceFactoryTest.getServerArgs(this.appKey, "127.0.0.1", NumUtil.nextPort());
        ZkRegistry zkRegistry = serviceFactoryTest.getZkRegistry();
        serverArgs.setRegistrys(ImmutableList.of(zkRegistry));

        ThriftServerPublisher publisher = new ThriftServerPublisher(serverArgs);
        Thread thread = new Thread(publisher::init);
        thread.setDaemon(true);
        thread.start();
        TimeUnit.MILLISECONDS.sleep(400);

        poolConfig = new GenericKeyedObjectPoolConfig();
        poolConfig.setMaxTotal(MAX_CONN);
        poolConfig.setMaxTotalPerKey(MAX_CONN);
        poolConfig.setMaxIdlePerKey(MAX_CONN);
        poolConfig.setMinIdlePerKey(MIN_CONN);
        poolConfig.setTestOnBorrow(true);
        poolConfig.setMinEvictableIdleTimeMillis(MINUTES.toMillis(1));
        poolConfig.setSoftMinEvictableIdleTimeMillis(MINUTES.toMillis(1));
        poolConfig.setJmxEnabled(false);
    }

    @Test
    public void testBlock() {
        ZkMonitor zkMonitor = new ZkMonitor();
        zkMonitor.setConnectString("localhost:2181");
        ClientArgs args = ClientArgs.builder()