[prev in list] [next in list] [prev in thread] [next in thread] 

List:       httpcomponents-commits
Subject:    [httpcomponents-client] 01/02: Support for connection TTL setting on a per-route basis
From:       olegk () apache ! org
Date:       2021-10-23 17:53:16
Message-ID: 20211023175315.514E880541 () gitbox ! apache ! org
[Download RAW message or body]

This is an automated email from the ASF dual-hosted git repository.

olegk pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/httpcomponents-client.git

commit a02455acb31ad7e676130d46d6a04bd97b76e957
Author: Oleg Kalnichevski <olegk@apache.org>
AuthorDate: Sat Oct 23 15:57:00 2021 +0200

    Support for connection TTL setting on a per-route basis
---
 .../hc/client5/http/config/ConnectionConfig.java   | 41 ++++++++++++--
 .../impl/io/BasicHttpClientConnectionManager.java  | 64 ++++++++++++++++------
 .../io/PoolingHttpClientConnectionManager.java     | 40 +++++++++-----
 .../PoolingHttpClientConnectionManagerBuilder.java | 11 ++--
 .../nio/PoolingAsyncClientConnectionManager.java   | 44 ++++++++++-----
 ...PoolingAsyncClientConnectionManagerBuilder.java | 10 +++-
 6 files changed, 153 insertions(+), 57 deletions(-)

diff --git a/httpclient5/src/main/java/org/apache/hc/client5/http/config/ConnectionConfig.java \
b/httpclient5/src/main/java/org/apache/hc/client5/http/config/ConnectionConfig.java \
                index 38ead6f..126b38d 100644
--- a/httpclient5/src/main/java/org/apache/hc/client5/http/config/ConnectionConfig.java
                
+++ b/httpclient5/src/main/java/org/apache/hc/client5/http/config/ConnectionConfig.java
 @@ -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 class ConnectionConfig implements Cloneable {
         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 class ConnectionConfig implements Cloneable {
         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 class ConnectionConfig implements Cloneable {
         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 class ConnectionConfig implements Cloneable {
         private Timeout socketTimeout;
         private Timeout connectTimeout;
         private TimeValue validateAfterInactivity;
+        private TimeValue timeToLive;
 
         Builder() {
             super();
@@ -190,11 +203,31 @@ public class ConnectionConfig implements Cloneable {
             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);
         }
 
     }
diff --git a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/io/BasicHttpClientConnectionManager.java \
b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/io/BasicHttpClientConnectionManager.java
 index a67b3ad..cfa82dc 100644
--- a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/io/BasicHttpClientConnectionManager.java
                
+++ b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/io/BasicHttpClientConnectionManager.java
 @@ -67,6 +67,7 @@ import org.apache.hc.core5.http.protocol.HttpContext;
 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 class \
BasicHttpClientConnectionManager implements HttpClientConnectionMan  \
this.connectionConfig = ConnectionConfig.DEFAULT;  this.tlsConfig = \
TlsConfig.DEFAULT;  this.closed = new AtomicBoolean(false);
-        this.validateAfterInactivity = TimeValue.ofSeconds(2L);
     }
 
     public BasicHttpClientConnectionManager(
@@ -210,6 +209,13 @@ public class BasicHttpClientConnectionManager implements \
HttpClientConnectionMan  /**
      * @since 5.2
      */
+    public synchronized TlsConfig getTlsConfig() {
+        return tlsConfig;
+    }
+
+    /**
+     * @since 5.2
+     */
     public synchronized void setTlsConfig(final TlsConfig tlsConfig) {
         this.tlsConfig = tlsConfig != null ? tlsConfig : TlsConfig.DEFAULT;
     }
@@ -260,21 +266,34 @@ public class BasicHttpClientConnectionManager implements \
HttpClientConnectionMan  }
 
     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 (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 (stale) {
-                if (LOG.isDebugEnabled()) {
-                    LOG.debug("{} connection {} is stale", id, \
ConnPoolSupport.getId(conn)); +        }
+        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 @@ public class BasicHttpClientConnectionManager implements \
HttpClientConnectionMan  validate();
         if (this.conn == null) {
             this.conn = this.connFactory.createConnection(null);
+            this.created = System.currentTimeMillis();
         } else {
             this.conn.activate();
         }
@@ -436,9 +456,12 @@ public class BasicHttpClientConnectionManager implements \
                HttpClientConnectionMan
      * @see #setValidateAfterInactivity(TimeValue)
      *
      * @since 5.1
+     *
+     * @deprecated Use {@link #getConnectionConfig()}
      */
+    @Deprecated
     public TimeValue getValidateAfterInactivity() {
-        return validateAfterInactivity;
+        return connectionConfig.getValidateAfterInactivity();
     }
 
     /**
@@ -448,9 +471,14 @@ public class BasicHttpClientConnectionManager implements \
                HttpClientConnectionMan
      * 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 {
diff --git a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/io/PoolingHttpClientConnectionManager.java \
b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/io/PoolingHttpClientConnectionManager.java
 index 8c415fc..626fe98 100644
--- a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/io/PoolingHttpClientConnectionManager.java
                
+++ b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/io/PoolingHttpClientConnectionManager.java
 @@ -76,6 +76,7 @@ import org.apache.hc.core5.pool.PoolStats;
 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 class PoolingHttpClientConnectionManager
                     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);
                             }
                         }
                     }
diff --git a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/io/PoolingHttpClientConnectionManagerBuilder.java \
b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/io/PoolingHttpClientConnectionManagerBuilder.java
 index c150e23..799c6ba 100644
--- a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/io/PoolingHttpClientConnectionManagerBuilder.java
                
+++ b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/io/PoolingHttpClientConnectionManagerBuilder.java
 @@ -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 class PoolingHttpClientConnectionManagerBuilder {
 
     /**
      * 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 class PoolingHttpClientConnectionManagerBuilder {
                         .build(),
                 poolConcurrencyPolicy,
                 poolReusePolicy,
-                timeToLive != null ? timeToLive : TimeValue.NEG_ONE_MILLISECOND,
+                null,
                 schemePortResolver,
                 dnsResolver,
                 connectionFactory);
diff --git a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/nio/PoolingAsyncClientConnectionManager.java \
b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/nio/PoolingAsyncClientConnectionManager.java
 index 3db4ca1..3975859 100644
--- a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/nio/PoolingAsyncClientConnectionManager.java
                
+++ b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/nio/PoolingAsyncClientConnectionManager.java
 @@ -88,6 +88,7 @@ import org.apache.hc.core5.reactor.ProtocolIOSession;
 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 class PoolingAsyncClientConnectionManager implements \
AsyncClientConnectio  
                         @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);
diff --git a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/nio/PoolingAsyncClientConnectionManagerBuilder.java \
b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/nio/PoolingAsyncClientConnectionManagerBuilder.java
 index 941f412..302e5af 100644
--- a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/nio/PoolingAsyncClientConnectionManagerBuilder.java
                
+++ b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/nio/PoolingAsyncClientConnectionManagerBuilder.java
 @@ -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 class PoolingAsyncClientConnectionManagerBuilder {
 
     /**
      * 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 class PoolingAsyncClientConnectionManagerBuilder {
                         .build(),
                 poolConcurrencyPolicy,
                 poolReusePolicy,
-                timeToLive,
+                null,
                 schemePortResolver,
                 dnsResolver);
         poolingmgr.setConnectionConfigResolver(connectionConfigResolver);


[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic