class AbstractPoolClient extends AbstractClient {
    private static ThreadPoolExecutor executor = new StandardThreadExecutor(1, 300, 20000,
            new DefaultThreadFactory("AbstractPoolClient-initPool-", true));
    protected static long defaultMinEvictableIdleTimeMillis = (long) 1000 * 60 * 60;//默认链接空闲时间
    protected static long defaultSoftMinEvictableIdleTimeMillis = (long) 1000 * 60 * 10;//
    protected static long defaultTimeBetweenEvictionRunsMillis = (long) 1000 * 60 * 10;//默认回收周期
    protected GenericObjectPool pool;
    protected GenericObjectPool.Config poolConfig;
    protected PoolableObjectFactory factory;

    public AbstractPoolClient(URL url) {
        super(url);
    }

    protected void initPool() {
        poolConfig = new GenericObjectPool.Config();
        poolConfig.minIdle =
                url.getIntParameter(URLParamType.minClientConnection.getName(), URLParamType.minClientConnection.getIntValue());
        poolConfig.maxIdle =
                url.getIntParameter(URLParamType.maxClientConnection.getName(), URLParamType.maxClientConnection.getIntValue());
        poolConfig.maxActive = poolConfig.maxIdle;
        poolConfig.maxWait = url.getIntParameter(URLParamType.requestTimeout.getName(), URLParamType.requestTimeout.getIntValue());
        poolConfig.lifo = url.getBooleanParameter(URLParamType.poolLifo.getName(), URLParamType.poolLifo.getBooleanValue());
        poolConfig.minEvictableIdleTimeMillis = defaultMinEvictableIdleTimeMillis;
        poolConfig.softMinEvictableIdleTimeMillis = defaultSoftMinEvictableIdleTimeMillis;
        poolConfig.timeBetweenEvictionRunsMillis = defaultTimeBetweenEvictionRunsMillis;
        factory = createChannelFactory();

        pool = new GenericObjectPool(factory, poolConfig);

        if (url.getBooleanParameter(URLParamType.lazyInit.getName(), URLParamType.lazyInit.getBooleanValue())) {
            state = ChannelState.ALIVE;
            LoggerUtil.debug("motan client will be lazily initialized. url:" + url.getUri());
            return;
        }
        initConnection(url.getBooleanParameter(URLParamType.asyncInitConnection.getName(), URLParamType.asyncInitConnection.getBooleanValue()));
    }

    protected void initConnection(boolean async) {
        if (async) {
            try {
                executor.execute(() -> {
                    createConnections();
                    LoggerUtil.info("async initPool {}. url:{}", state == ChannelState.ALIVE ? "success" : "fail", getUrl().getUri());
                });
                return;
            } catch (Exception e) {
                LoggerUtil.error("async initPool task execute fail!" + this.url.getUri(), e);
            }
        }
        createConnections();
    }

    private void createConnections() {
        for (int i = 0; i < poolConfig.minIdle; i++) {
            try {
                pool.addObject();
                state = ChannelState.ALIVE;// if any channel is successfully created, the ALIVE status will be set
            } catch (Exception e) {
                LoggerUtil.error("NettyClient init pool create connect Error: url=" + url.getUri(), e);
            }
        }
        if (state == ChannelState.UNINIT) {
            state = ChannelState.UNALIVE; // set state to UNALIVE, so that the heartbeat can be triggered if failed to create connections.
        }
    }


    protected abstract BasePoolableObjectFactory createChannelFactory();

    protected Channel borrowObject() throws Exception {
        Channel nettyChannel = (Channel) pool.borrowObject();

        if (nettyChannel != null && nettyChannel.isAvailable()) {
            return nettyChannel;
        }

        invalidateObject(nettyChannel);

        String errorMsg = this.getClass().getSimpleName() + " borrowObject Error: url=" + url.getUri();
        LoggerUtil.error(errorMsg);
        throw new MotanServiceException(errorMsg);
    }

    protected void invalidateObject(Channel nettyChannel) {
        if (nettyChannel == null) {
            return;
        }
        try {
            pool.invalidateObject(nettyChannel);
        } catch (Exception ie) {
            LoggerUtil.error(this.getClass().getSimpleName() + " invalidate client Error: url=" + url.getUri(), ie);
        }
    }

    protected void returnObject(Channel channel) {<FILL_FUNCTION_BODY>}

}

        if (channel == null) {
            return;
        }

        try {
            pool.returnObject(channel);
        } catch (Exception ie) {
            LoggerUtil.error(this.getClass().getSimpleName() + " return client Error: url=" + url.getUri(), ie);
        }
    
<methods>public void <init>(com.weibo.api.motan.rpc.URL) ,public java.net.InetSocketAddress getLocalAddress() ,public java.net.InetSocketAddress getRemoteAddress() ,public void heartbeat(com.weibo.api.motan.rpc.Request) ,public void setLocalAddress(java.net.InetSocketAddress) ,public void setRemoteAddress(java.net.InetSocketAddress) <variables>protected com.weibo.api.motan.codec.Codec codec,protected java.net.InetSocketAddress localAddress,protected java.net.InetSocketAddress remoteAddress,protected volatile com.weibo.api.motan.common.ChannelState state,protected com.weibo.api.motan.rpc.URL url