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();
}
}