Skip to content

Commit 4681610

Browse files
committed
ConnectionConfig: Add idleTimeout
Connection TTL can be effected either in bulk by calling `connMgr.closeExpired()` or at connection reuse time by configuring a `timeToLive` on the `ConnectionConfig`. Strangely, idle timeouts can only be effected in bulk via `connMgr.closeIdle()`, or with an `IdleConnectionEvictor`; they have no corresponding `ConnectionConfig` setting. This change resolves this inconsistency. The result is that idle timeouts are much easier to configure and enforce: they don't require spinning up a background thread that periodically locks the entire conn pool and closes idle connections; there is no longer the possibility of reusing a connection after it hits its intended idle timeout; and it doesn't matter whether the connection pool is shared.
1 parent 665325c commit 4681610

File tree

3 files changed

+59
-1
lines changed

3 files changed

+59
-1
lines changed

httpclient5/src/main/java/org/apache/hc/client5/http/config/ConnectionConfig.java

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,24 +48,27 @@ public class ConnectionConfig implements Cloneable {
4848

4949
private final Timeout connectTimeout;
5050
private final Timeout socketTimeout;
51+
private final Timeout idleTimeout;
5152
private final TimeValue validateAfterInactivity;
5253
private final TimeValue timeToLive;
5354

5455
/**
5556
* Intended for CDI compatibility
5657
*/
5758
protected ConnectionConfig() {
58-
this(DEFAULT_CONNECT_TIMEOUT, null, null, null);
59+
this(DEFAULT_CONNECT_TIMEOUT, null, null, null, null);
5960
}
6061

6162
ConnectionConfig(
6263
final Timeout connectTimeout,
6364
final Timeout socketTimeout,
65+
final Timeout idleTimeout,
6466
final TimeValue validateAfterInactivity,
6567
final TimeValue timeToLive) {
6668
super();
6769
this.connectTimeout = connectTimeout;
6870
this.socketTimeout = socketTimeout;
71+
this.idleTimeout = idleTimeout;
6972
this.validateAfterInactivity = validateAfterInactivity;
7073
this.timeToLive = timeToLive;
7174
}
@@ -84,6 +87,13 @@ public Timeout getConnectTimeout() {
8487
return connectTimeout;
8588
}
8689

90+
/**
91+
* @see Builder#setIdleTimeout(Timeout)
92+
*/
93+
public Timeout getIdleTimeout() {
94+
return idleTimeout;
95+
}
96+
8797
/**
8898
* @see Builder#setValidateAfterInactivity(TimeValue)
8999
*/
@@ -109,6 +119,7 @@ public String toString() {
109119
builder.append("[");
110120
builder.append("connectTimeout=").append(connectTimeout);
111121
builder.append(", socketTimeout=").append(socketTimeout);
122+
builder.append(", idleTimeout=").append(idleTimeout);
112123
builder.append(", validateAfterInactivity=").append(validateAfterInactivity);
113124
builder.append(", timeToLive=").append(timeToLive);
114125
builder.append("]");
@@ -131,6 +142,7 @@ public static class Builder {
131142

132143
private Timeout socketTimeout;
133144
private Timeout connectTimeout;
145+
private Timeout idleTimeout;
134146
private TimeValue validateAfterInactivity;
135147
private TimeValue timeToLive;
136148

@@ -194,6 +206,35 @@ public Builder setConnectTimeout(final long connectTimeout, final TimeUnit timeU
194206
return this;
195207
}
196208

209+
/**
210+
* Determines the maximum period of idleness for a connection.
211+
* Connections that are idle for longer than {@code idleTimeout} are no
212+
* longer eligible for reuse.
213+
* <p>
214+
* A timeout value of zero is interpreted as an infinite timeout.
215+
* </p>
216+
* <p>
217+
* Default: {@code null} (undefined)
218+
* </p>
219+
*
220+
* @return this instance.
221+
*
222+
* @since 5.6
223+
*/
224+
public Builder setIdleTimeout(final Timeout idleTimeout) {
225+
this.idleTimeout = idleTimeout;
226+
return this;
227+
}
228+
229+
/**
230+
* @return this instance.
231+
* @see #setIdleTimeout(Timeout)
232+
*/
233+
public Builder setIdleTimeout(final long idleTimeout, final TimeUnit timeUnit) {
234+
this.idleTimeout = Timeout.of(idleTimeout, timeUnit);
235+
return this;
236+
}
237+
197238
/**
198239
* Defines period of inactivity after which persistent connections must
199240
* be re-validated prior to being leased to the consumer. Negative values passed
@@ -244,6 +285,7 @@ public ConnectionConfig build() {
244285
return new ConnectionConfig(
245286
connectTimeout != null ? connectTimeout : DEFAULT_CONNECT_TIMEOUT,
246287
socketTimeout,
288+
idleTimeout,
247289
validateAfterInactivity,
248290
timeToLive);
249291
}

httpclient5/src/main/java/org/apache/hc/client5/http/impl/io/PoolingHttpClientConnectionManager.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -391,6 +391,14 @@ public ConnectionEndpoint get(
391391
}
392392
}
393393
}
394+
if (poolEntry.hasConnection()) {
395+
final TimeValue idleTimeout = connectionConfig.getIdleTimeout();
396+
if (TimeValue.isPositive(idleTimeout)) {
397+
if (Deadline.calculate(poolEntry.getUpdated(), idleTimeout).isExpired()) {
398+
poolEntry.discardConnection(CloseMode.GRACEFUL);
399+
}
400+
}
401+
}
394402
if (poolEntry.hasConnection()) {
395403
final TimeValue timeValue = resolveValidateAfterInactivity(connectionConfig);
396404
if (TimeValue.isNonNegative(timeValue)) {

httpclient5/src/main/java/org/apache/hc/client5/http/impl/nio/PoolingAsyncClientConnectionManager.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -300,6 +300,14 @@ public void completed(final PoolEntry<HttpRoute, ManagedAsyncClientConnection> p
300300
}
301301
}
302302
}
303+
if (poolEntry.hasConnection()) {
304+
final TimeValue idleTimeout = connectionConfig.getIdleTimeout();
305+
if (TimeValue.isPositive(idleTimeout)) {
306+
if (Deadline.calculate(poolEntry.getUpdated(), idleTimeout).isExpired()) {
307+
poolEntry.discardConnection(CloseMode.GRACEFUL);
308+
}
309+
}
310+
}
303311
if (poolEntry.hasConnection()) {
304312
final ManagedAsyncClientConnection connection = poolEntry.getConnection();
305313
final TimeValue timeValue = connectionConfig.getValidateAfterInactivity();

0 commit comments

Comments
 (0)