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

com.firefly.client.http2.HttpClientConnectionManager Maven / Gradle / Ivy

The newest version!
package com.firefly.client.http2;

import com.firefly.codec.http2.model.HttpVersion;
import com.firefly.utils.Assert;
import com.firefly.utils.StringUtils;
import com.firefly.utils.io.IO;
import com.firefly.utils.lang.AbstractLifeCycle;
import com.firefly.utils.lang.pool.Pool;
import com.firefly.utils.lang.pool.PooledObject;
import com.firefly.utils.lang.pool.BoundObjectPool;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.concurrent.*;

/**
 * @author Pengtao Qiu
 */
public class HttpClientConnectionManager extends AbstractLifeCycle {

    protected static Logger log = LoggerFactory.getLogger("firefly-system");

    private final HTTP2Client client;
    private final String host;
    private final int port;
    private final SimpleHTTPClientConfiguration configuration;
    private volatile CompletableFuture> currentConnReq;
    private volatile HTTPClientConnection connection;
    private BoundObjectPool pool;
    private final ExecutorService gettingService;

    public HttpClientConnectionManager(HTTP2Client client,
                                       String host, int port,
                                       SimpleHTTPClientConfiguration configuration) {
        this.client = client;
        this.host = host;
        this.port = port;
        this.configuration = configuration;
        this.currentConnReq = client.connect(host, port).thenApply(FakePooledObject::new);
        this.gettingService = Executors.newSingleThreadExecutor(r -> new Thread(r, "firefly-http-connection-manager-thread"));
    }

    private class FakePooledObject extends PooledObject {

        FakePooledObject(HTTPClientConnection connection) {
            super(connection, null, null);
        }

        public void release() {
            if (object.getHttpVersion() != HttpVersion.HTTP_2) {
                IO.close(object);
            }
        }

        public void clear() {
        }

        public void register() {
        }
    }

    public CompletableFuture> asyncGet() {
        if (currentConnReq.isDone()) {
            return asyncGetFromPool();
        } else {
            CompletableFuture> future = new CompletableFuture<>();
            gettingService.submit(() -> {
                try {
                    future.complete(asyncGetFromPool().get());
                } catch (InterruptedException | ExecutionException e) {
                    future.completeExceptionally(e);
                }
            });
            return future;
        }
    }

    private CompletableFuture> asyncGetFromPool() {
        if (connection == null) {
            try {
                connection = currentConnReq.get(configuration.getConnectTimeout(), TimeUnit.SECONDS).getObject();
            } catch (InterruptedException | ExecutionException | TimeoutException e) {
                IO.close(connection);
            }
        }
        Assert.state(connection != null, "Get the HTTP connection exception");

        if (connection.getHttpVersion() == HttpVersion.HTTP_2) {
            return asyncGetHTTP2Conn();
        } else {
            return asyncGetHTTP1Conn();
        }
    }

    private CompletableFuture> asyncGetHTTP1Conn() {
        start();
        return pool.asyncGet();
    }

    private CompletableFuture> asyncGetHTTP2Conn() {
        if (connection.isOpen()) {
            CompletableFuture> ret = new CompletableFuture<>();
            ret.complete(new FakePooledObject(connection));
            return ret;
        } else {
            currentConnReq = client.connect(host, port).thenApply(FakePooledObject::new);
            return currentConnReq;
        }
    }

    public int size() {
        return pool.size();
    }

    @Override
    protected void init() {
        Pool.Validator validator = conn -> conn.getObject().isOpen();
        Pool.Dispose dispose = conn -> IO.close(conn.getObject());
        Pool.ObjectFactory factory = pool -> {
            CompletableFuture> future = new CompletableFuture<>();
            client.connect(host, port).thenAccept(conn -> {
                String leakMessage = StringUtils.replace(
                        "The Firefly HTTP client connection leaked. id -> {}, host -> {}:{}",
                        conn.getSessionId(), host, port);
                PooledObject pooledObject = new PooledObject<>(conn, pool, pooledObj -> { // connection leak callback
                    log.warn(leakMessage);
                    pool.getCreatedCount().decrementAndGet();
                    IO.close(pooledObj.getObject());
                });
                conn.onClose(c -> pooledObject.release())
                    .onException((c, exception) -> pooledObject.release());
                future.complete(pooledObject);
            }).exceptionally(ex -> {
                future.completeExceptionally(ex);
                return null;
            });
            return future;
        };

        pool = new BoundObjectPool<>(
                configuration.getPoolSize(), configuration.getConnectTimeout(), configuration.getLeakDetectorInterval(), configuration.getReleaseTimeout(),
                configuration.getMaxGettingThreadNum(), configuration.getMaxReleaseThreadNum(),
                factory, validator, dispose,
                () -> log.info("The Firefly HTTP client has not any connections leaked. host -> {}:{}", host, port));
    }

    @Override
    protected void destroy() {
        if (pool != null) {
            pool.stop();
        }
        gettingService.shutdown();
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy