mirror of
https://github.com/apache/httpcomponents-client.git
synced 2025-02-16 15:07:27 +00:00
Support for connection TTL setting on a per-route basis
This commit is contained in:
parent
fff097615b
commit
a02455acb3
@ -49,22 +49,25 @@ public class ConnectionConfig implements Cloneable {
|
||||
private final Timeout connectTimeout;
|
||||
private final Timeout socketTimeout;
|
||||
private final TimeValue validateAfterInactivity;
|
||||
private final TimeValue timeToLive;
|
||||
|
||||
/**
|
||||
* Intended for CDI compatibility
|
||||
*/
|
||||
protected ConnectionConfig() {
|
||||
this(DEFAULT_CONNECT_TIMEOUT, null, null);
|
||||
this(DEFAULT_CONNECT_TIMEOUT, null, null, null);
|
||||
}
|
||||
|
||||
ConnectionConfig(
|
||||
final Timeout connectTimeout,
|
||||
final Timeout socketTimeout,
|
||||
final TimeValue validateAfterInactivity) {
|
||||
final TimeValue validateAfterInactivity,
|
||||
final TimeValue timeToLive) {
|
||||
super();
|
||||
this.connectTimeout = connectTimeout;
|
||||
this.socketTimeout = socketTimeout;
|
||||
this.validateAfterInactivity = validateAfterInactivity;
|
||||
this.timeToLive = timeToLive;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -88,6 +91,13 @@ public TimeValue getValidateAfterInactivity() {
|
||||
return validateAfterInactivity;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see Builder#setTimeToLive(TimeValue) (TimeValue)
|
||||
*/
|
||||
public TimeValue getTimeToLive() {
|
||||
return timeToLive;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ConnectionConfig clone() throws CloneNotSupportedException {
|
||||
return (ConnectionConfig) super.clone();
|
||||
@ -100,6 +110,7 @@ public String toString() {
|
||||
builder.append(", connectTimeout=").append(connectTimeout);
|
||||
builder.append(", socketTimeout=").append(socketTimeout);
|
||||
builder.append(", validateAfterInactivity=").append(validateAfterInactivity);
|
||||
builder.append(", timeToLive=").append(timeToLive);
|
||||
builder.append("]");
|
||||
return builder.toString();
|
||||
}
|
||||
@ -112,7 +123,8 @@ public static ConnectionConfig.Builder copy(final ConnectionConfig config) {
|
||||
return new Builder()
|
||||
.setConnectTimeout(config.getConnectTimeout())
|
||||
.setSocketTimeout(config.getSocketTimeout())
|
||||
.setValidateAfterInactivity(config.getValidateAfterInactivity());
|
||||
.setValidateAfterInactivity(config.getValidateAfterInactivity())
|
||||
.setTimeToLive(config.getTimeToLive());
|
||||
}
|
||||
|
||||
public static class Builder {
|
||||
@ -120,6 +132,7 @@ public static class Builder {
|
||||
private Timeout socketTimeout;
|
||||
private Timeout connectTimeout;
|
||||
private TimeValue validateAfterInactivity;
|
||||
private TimeValue timeToLive;
|
||||
|
||||
Builder() {
|
||||
super();
|
||||
@ -190,11 +203,31 @@ public Builder setValidateAfterInactivity(final long validateAfterInactivity, fi
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines the total span of time connections can be kept alive or execute requests.
|
||||
* <p>
|
||||
* Default: {@code null} (undefined)
|
||||
* </p>
|
||||
*/
|
||||
public Builder setTimeToLive(final TimeValue timeToLive) {
|
||||
this.timeToLive = timeToLive;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see #setTimeToLive(TimeValue)
|
||||
*/
|
||||
public Builder setTimeToLive(final long timeToLive, final TimeUnit timeUnit) {
|
||||
this.timeToLive = TimeValue.of(timeToLive, timeUnit);
|
||||
return this;
|
||||
}
|
||||
|
||||
public ConnectionConfig build() {
|
||||
return new ConnectionConfig(
|
||||
connectTimeout != null ? connectTimeout : DEFAULT_CONNECT_TIMEOUT,
|
||||
socketTimeout,
|
||||
validateAfterInactivity);
|
||||
validateAfterInactivity,
|
||||
timeToLive);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -67,6 +67,7 @@
|
||||
import org.apache.hc.core5.io.CloseMode;
|
||||
import org.apache.hc.core5.util.Args;
|
||||
import org.apache.hc.core5.util.Asserts;
|
||||
import org.apache.hc.core5.util.Deadline;
|
||||
import org.apache.hc.core5.util.LangUtils;
|
||||
import org.apache.hc.core5.util.TimeValue;
|
||||
import org.apache.hc.core5.util.Timeout;
|
||||
@ -105,6 +106,7 @@ public class BasicHttpClientConnectionManager implements HttpClientConnectionMan
|
||||
private ManagedHttpClientConnection conn;
|
||||
private HttpRoute route;
|
||||
private Object state;
|
||||
private long created;
|
||||
private long updated;
|
||||
private long expiry;
|
||||
private boolean leased;
|
||||
@ -114,8 +116,6 @@ public class BasicHttpClientConnectionManager implements HttpClientConnectionMan
|
||||
|
||||
private final AtomicBoolean closed;
|
||||
|
||||
private volatile TimeValue validateAfterInactivity;
|
||||
|
||||
private static Registry<ConnectionSocketFactory> getDefaultRegistry() {
|
||||
return RegistryBuilder.<ConnectionSocketFactory>create()
|
||||
.register(URIScheme.HTTP.id, PlainConnectionSocketFactory.getSocketFactory())
|
||||
@ -147,7 +147,6 @@ public BasicHttpClientConnectionManager(
|
||||
this.connectionConfig = ConnectionConfig.DEFAULT;
|
||||
this.tlsConfig = TlsConfig.DEFAULT;
|
||||
this.closed = new AtomicBoolean(false);
|
||||
this.validateAfterInactivity = TimeValue.ofSeconds(2L);
|
||||
}
|
||||
|
||||
public BasicHttpClientConnectionManager(
|
||||
@ -207,6 +206,13 @@ public synchronized void setConnectionConfig(final ConnectionConfig connectionCo
|
||||
this.connectionConfig = connectionConfig != null ? connectionConfig : ConnectionConfig.DEFAULT;
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 5.2
|
||||
*/
|
||||
public synchronized TlsConfig getTlsConfig() {
|
||||
return tlsConfig;
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 5.2
|
||||
*/
|
||||
@ -260,21 +266,34 @@ private void checkExpiry() {
|
||||
}
|
||||
|
||||
private void validate() {
|
||||
final TimeValue validateAfterInactivitySnapshot = validateAfterInactivity;
|
||||
if (this.conn != null
|
||||
&& TimeValue.isNonNegative(validateAfterInactivitySnapshot)
|
||||
&& updated + validateAfterInactivitySnapshot.toMilliseconds() <= System.currentTimeMillis()) {
|
||||
boolean stale;
|
||||
try {
|
||||
stale = conn.isStale();
|
||||
} catch (final IOException ignore) {
|
||||
stale = true;
|
||||
}
|
||||
if (stale) {
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("{} connection {} is stale", id, ConnPoolSupport.getId(conn));
|
||||
if (this.conn != null) {
|
||||
final TimeValue timeToLive = connectionConfig.getTimeToLive();
|
||||
if (TimeValue.isNonNegative(timeToLive)) {
|
||||
final Deadline deadline = Deadline.calculate(created, timeToLive);
|
||||
if (deadline.isExpired()) {
|
||||
closeConnection(CloseMode.GRACEFUL);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (this.conn != null) {
|
||||
final TimeValue timeValue = connectionConfig.getValidateAfterInactivity() != null ?
|
||||
connectionConfig.getValidateAfterInactivity() : TimeValue.ofSeconds(2);
|
||||
if (TimeValue.isNonNegative(timeValue)) {
|
||||
final Deadline deadline = Deadline.calculate(updated, timeValue);
|
||||
if (deadline.isExpired()) {
|
||||
boolean stale;
|
||||
try {
|
||||
stale = conn.isStale();
|
||||
} catch (final IOException ignore) {
|
||||
stale = true;
|
||||
}
|
||||
if (stale) {
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("{} connection {} is stale", id, ConnPoolSupport.getId(conn));
|
||||
}
|
||||
closeConnection(CloseMode.GRACEFUL);
|
||||
}
|
||||
}
|
||||
closeConnection(CloseMode.GRACEFUL);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -294,6 +313,7 @@ synchronized ManagedHttpClientConnection getConnection(final HttpRoute route, fi
|
||||
validate();
|
||||
if (this.conn == null) {
|
||||
this.conn = this.connFactory.createConnection(null);
|
||||
this.created = System.currentTimeMillis();
|
||||
} else {
|
||||
this.conn.activate();
|
||||
}
|
||||
@ -436,9 +456,12 @@ public synchronized void closeIdle(final TimeValue idleTime) {
|
||||
* @see #setValidateAfterInactivity(TimeValue)
|
||||
*
|
||||
* @since 5.1
|
||||
*
|
||||
* @deprecated Use {@link #getConnectionConfig()}
|
||||
*/
|
||||
@Deprecated
|
||||
public TimeValue getValidateAfterInactivity() {
|
||||
return validateAfterInactivity;
|
||||
return connectionConfig.getValidateAfterInactivity();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -448,9 +471,14 @@ public TimeValue getValidateAfterInactivity() {
|
||||
* detect connections that have become stale (half-closed) while kept inactive in the pool.
|
||||
*
|
||||
* @since 5.1
|
||||
*
|
||||
* @deprecated Use {@link #setConnectionConfig(ConnectionConfig)}
|
||||
*/
|
||||
@Deprecated
|
||||
public void setValidateAfterInactivity(final TimeValue validateAfterInactivity) {
|
||||
this.validateAfterInactivity = validateAfterInactivity;
|
||||
this.connectionConfig = ConnectionConfig.custom()
|
||||
.setValidateAfterInactivity(validateAfterInactivity)
|
||||
.build();
|
||||
}
|
||||
|
||||
class InternalConnectionEndpoint extends ConnectionEndpoint {
|
||||
|
@ -76,6 +76,7 @@
|
||||
import org.apache.hc.core5.pool.StrictConnPool;
|
||||
import org.apache.hc.core5.util.Args;
|
||||
import org.apache.hc.core5.util.Asserts;
|
||||
import org.apache.hc.core5.util.Deadline;
|
||||
import org.apache.hc.core5.util.Identifiable;
|
||||
import org.apache.hc.core5.util.TimeValue;
|
||||
import org.apache.hc.core5.util.Timeout;
|
||||
@ -303,23 +304,34 @@ public synchronized ConnectionEndpoint get(
|
||||
LOG.debug("{} endpoint leased {}", id, ConnPoolSupport.formatStats(route, state, pool));
|
||||
}
|
||||
final ConnectionConfig connectionConfig = resolveConnectionConfig(route);
|
||||
final TimeValue timeValue = resolveValidateAfterInactivity(connectionConfig);
|
||||
try {
|
||||
if (TimeValue.isNonNegative(timeValue)) {
|
||||
final ManagedHttpClientConnection conn = poolEntry.getConnection();
|
||||
if (conn != null
|
||||
&& poolEntry.getUpdated() + timeValue.toMilliseconds() <= System.currentTimeMillis()) {
|
||||
boolean stale;
|
||||
try {
|
||||
stale = conn.isStale();
|
||||
} catch (final IOException ignore) {
|
||||
stale = true;
|
||||
if (poolEntry.hasConnection()) {
|
||||
final TimeValue timeToLive = connectionConfig.getTimeToLive();
|
||||
if (TimeValue.isNonNegative(timeToLive)) {
|
||||
final Deadline deadline = Deadline.calculate(poolEntry.getCreated(), timeToLive);
|
||||
if (deadline.isExpired()) {
|
||||
poolEntry.discardConnection(CloseMode.GRACEFUL);
|
||||
}
|
||||
if (stale) {
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("{} connection {} is stale", id, ConnPoolSupport.getId(conn));
|
||||
}
|
||||
}
|
||||
if (poolEntry.hasConnection()) {
|
||||
final TimeValue timeValue = resolveValidateAfterInactivity(connectionConfig);
|
||||
if (TimeValue.isNonNegative(timeValue)) {
|
||||
final Deadline deadline = Deadline.calculate(poolEntry.getUpdated(), timeValue);
|
||||
if (deadline.isExpired()) {
|
||||
final ManagedHttpClientConnection conn = poolEntry.getConnection();
|
||||
boolean stale;
|
||||
try {
|
||||
stale = conn.isStale();
|
||||
} catch (final IOException ignore) {
|
||||
stale = true;
|
||||
}
|
||||
if (stale) {
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("{} connection {} is stale", id, ConnPoolSupport.getId(conn));
|
||||
}
|
||||
poolEntry.discardConnection(CloseMode.IMMEDIATE);
|
||||
}
|
||||
poolEntry.discardConnection(CloseMode.IMMEDIATE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -90,8 +90,6 @@ public class PoolingHttpClientConnectionManagerBuilder {
|
||||
private int maxConnTotal;
|
||||
private int maxConnPerRoute;
|
||||
|
||||
private TimeValue timeToLive;
|
||||
|
||||
public static PoolingHttpClientConnectionManagerBuilder create() {
|
||||
return new PoolingHttpClientConnectionManagerBuilder();
|
||||
}
|
||||
@ -229,9 +227,14 @@ public final PoolingHttpClientConnectionManagerBuilder setTlsConfigResolver(
|
||||
|
||||
/**
|
||||
* Sets maximum time to live for persistent connections
|
||||
*
|
||||
* @deprecated Use {@link #setDefaultConnectionConfig(ConnectionConfig)}.
|
||||
*/
|
||||
@Deprecated
|
||||
public final PoolingHttpClientConnectionManagerBuilder setConnectionTimeToLive(final TimeValue timeToLive) {
|
||||
this.timeToLive = timeToLive;
|
||||
setDefaultConnectionConfig(ConnectionConfig.custom()
|
||||
.setTimeToLive(timeToLive)
|
||||
.build());
|
||||
return this;
|
||||
}
|
||||
|
||||
@ -269,7 +272,7 @@ public PoolingHttpClientConnectionManager build() {
|
||||
.build(),
|
||||
poolConcurrencyPolicy,
|
||||
poolReusePolicy,
|
||||
timeToLive != null ? timeToLive : TimeValue.NEG_ONE_MILLISECOND,
|
||||
null,
|
||||
schemePortResolver,
|
||||
dnsResolver,
|
||||
connectionFactory);
|
||||
|
@ -88,6 +88,7 @@
|
||||
import org.apache.hc.core5.reactor.ssl.TlsDetails;
|
||||
import org.apache.hc.core5.util.Args;
|
||||
import org.apache.hc.core5.util.Asserts;
|
||||
import org.apache.hc.core5.util.Deadline;
|
||||
import org.apache.hc.core5.util.Identifiable;
|
||||
import org.apache.hc.core5.util.TimeValue;
|
||||
import org.apache.hc.core5.util.Timeout;
|
||||
@ -261,25 +262,40 @@ public Future<AsyncConnectionEndpoint> lease(
|
||||
|
||||
@Override
|
||||
public void completed(final PoolEntry<HttpRoute, ManagedAsyncClientConnection> poolEntry) {
|
||||
final ManagedAsyncClientConnection connection = poolEntry.getConnection();
|
||||
final TimeValue timeValue = connectionConfig != null ? connectionConfig.getValidateAfterInactivity() : null;
|
||||
if (TimeValue.isNonNegative(timeValue) && connection != null &&
|
||||
poolEntry.getUpdated() + timeValue.toMilliseconds() <= System.currentTimeMillis()) {
|
||||
final ProtocolVersion protocolVersion = connection.getProtocolVersion();
|
||||
if (protocolVersion != null && protocolVersion.greaterEquals(HttpVersion.HTTP_2_0)) {
|
||||
connection.submitCommand(new PingCommand(new BasicPingHandler(result -> {
|
||||
if (result == null || !result) {
|
||||
if (poolEntry.hasConnection()) {
|
||||
final TimeValue timeToLive = connectionConfig.getTimeToLive();
|
||||
if (TimeValue.isNonNegative(timeToLive)) {
|
||||
final Deadline deadline = Deadline.calculate(poolEntry.getCreated(), timeToLive);
|
||||
if (deadline.isExpired()) {
|
||||
poolEntry.discardConnection(CloseMode.GRACEFUL);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (poolEntry.hasConnection()) {
|
||||
final ManagedAsyncClientConnection connection = poolEntry.getConnection();
|
||||
final TimeValue timeValue = connectionConfig.getValidateAfterInactivity();
|
||||
if (connection.isOpen() && TimeValue.isNonNegative(timeValue)) {
|
||||
final Deadline deadline = Deadline.calculate(poolEntry.getUpdated(), timeValue);
|
||||
if (deadline.isExpired()) {
|
||||
final ProtocolVersion protocolVersion = connection.getProtocolVersion();
|
||||
if (protocolVersion != null && protocolVersion.greaterEquals(HttpVersion.HTTP_2_0)) {
|
||||
connection.submitCommand(new PingCommand(new BasicPingHandler(result -> {
|
||||
if (result == null || !result) {
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("{} connection {} is stale", id, ConnPoolSupport.getId(connection));
|
||||
}
|
||||
poolEntry.discardConnection(CloseMode.GRACEFUL);
|
||||
}
|
||||
leaseCompleted(poolEntry);
|
||||
})), Command.Priority.IMMEDIATE);
|
||||
return;
|
||||
} else {
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("{} connection {} is stale", id, ConnPoolSupport.getId(connection));
|
||||
LOG.debug("{} connection {} is closed", id, ConnPoolSupport.getId(connection));
|
||||
}
|
||||
poolEntry.discardConnection(CloseMode.IMMEDIATE);
|
||||
}
|
||||
})), Command.Priority.IMMEDIATE);
|
||||
} else {
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("{} connection {} is closed", id, ConnPoolSupport.getId(connection));
|
||||
}
|
||||
poolEntry.discardConnection(CloseMode.IMMEDIATE);
|
||||
}
|
||||
}
|
||||
leaseCompleted(poolEntry);
|
||||
|
@ -87,7 +87,6 @@ public class PoolingAsyncClientConnectionManagerBuilder {
|
||||
private Resolver<HttpRoute, SocketConfig> socketConfigResolver;
|
||||
private Resolver<HttpRoute, ConnectionConfig> connectionConfigResolver;
|
||||
private Resolver<HttpHost, TlsConfig> tlsConfigResolver;
|
||||
private TimeValue timeToLive;
|
||||
|
||||
public static PoolingAsyncClientConnectionManagerBuilder create() {
|
||||
return new PoolingAsyncClientConnectionManagerBuilder();
|
||||
@ -198,9 +197,14 @@ public final PoolingAsyncClientConnectionManagerBuilder setTlsConfigResolver(
|
||||
|
||||
/**
|
||||
* Sets maximum time to live for persistent connections
|
||||
*
|
||||
* @deprecated Use {@link #setDefaultConnectionConfig(ConnectionConfig)}
|
||||
*/
|
||||
@Deprecated
|
||||
public final PoolingAsyncClientConnectionManagerBuilder setConnectionTimeToLive(final TimeValue timeToLive) {
|
||||
this.timeToLive = timeToLive;
|
||||
setDefaultConnectionConfig(ConnectionConfig.custom()
|
||||
.setTimeToLive(timeToLive)
|
||||
.build());
|
||||
return this;
|
||||
}
|
||||
|
||||
@ -252,7 +256,7 @@ public PoolingAsyncClientConnectionManager build() {
|
||||
.build(),
|
||||
poolConcurrencyPolicy,
|
||||
poolReusePolicy,
|
||||
timeToLive,
|
||||
null,
|
||||
schemePortResolver,
|
||||
dnsResolver);
|
||||
poolingmgr.setConnectionConfigResolver(connectionConfigResolver);
|
||||
|
Loading…
x
Reference in New Issue
Block a user