Skip to content

Commit 32337e4

Browse files
committed
Updated connection lifecycle behavior.
1. Added support for `useIdleConnectionReaper`, `connectionTimeToLive` and `connectionMaxIdleTime` to the Netty HTTP client. 2. Enable the idle connection reaper by default for apache and netty clients. Connection time-to-live remains disabled by default. Fixes #856
1 parent 6e1c381 commit 32337e4

21 files changed

+742
-110
lines changed
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"category": "AWS SDK for Java v2",
3+
"type": "feature",
4+
"description": "Add support for `connectionTimeToLive`, `connectionMaxIdleTime` and `useIdleConnectionReaper` to the netty HTTP client."
5+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"category": "AWS SDK for Java v2",
3+
"type": "feature",
4+
"description": "Enable `useIdleConnectionReaper` by default for Netty and Apache."
5+
}

docs/LaunchChangelog.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -219,12 +219,12 @@ DynamoDbAsyncClient client =
219219
| Max Connections | `clientConfig.setMaxConnections(...)`<br />`clientConfig.withMaxConnections(...)` | `httpClientBuilder.maxConnections(...)` | `httpClientBuilder.maxConcurrency(...)` |
220220
| Connection Timeout | `clientConfig.setConnectionTimeout(...)`<br />`clientConfig.withConnectionTimeout(...)` | `httpClientBuilder.connectionTimeout(...)` | `httpClientBuilder.connectionTimeout(...)` |
221221
| Socket Timeout | `clientConfig.setSocketTimeout(...)`<br />`clientConfig.withSocketTimeout(...)` | `httpClientBuilder.socketTimeout(...)` | `httpClientBuilder.writeTimeout(...)` <br /> `httpClientBuilder.readTimeout(...)` |
222-
| Connection TTL | `clientConfig.setConnectionTTL(...)`<br />`clientConfig.withConnectionTTL(...)` | `httpClientBuilder.connectionTimeToLive(...)` | [Not Supported](https://github.com/aws/aws-sdk-java-v2/issues/856) |
223-
| Connection Max Idle | `clientConfig.setConnectionMaxIdleMillis(...)`<br />`clientConfig.withConnectionMaxIdleMillis(...)` | `httpClientBuilder.connectionMaxIdleTime(...)` | [Not Supported](https://github.com/aws/aws-sdk-java-v2/issues/856) |
222+
| Connection TTL | `clientConfig.setConnectionTTL(...)`<br />`clientConfig.withConnectionTTL(...)` | `httpClientBuilder.connectionTimeToLive(...)` | `httpClientBuilder.connectionTimeToLive(...)` |
223+
| Connection Max Idle | `clientConfig.setConnectionMaxIdleMillis(...)`<br />`clientConfig.withConnectionMaxIdleMillis(...)` | `httpClientBuilder.connectionMaxIdleTime(...)` | `httpClientBuilder.connectionMaxIdleTime(...)` |
224224
| Validate After Inactivity | `clientConfig.setValidateAfterInactivityMillis(...)`<br />`clientConfig.withValidateAfterInactivityMillis(...)` | Not Supported ([Request Feature](https://github.com/aws/aws-sdk-java-v2/issues/new)) | Not Supported ([Request Feature](https://github.com/aws/aws-sdk-java-v2/issues/new)) |
225225
| Local Address | `clientConfig.setLocalAddress(...)`<br />`clientConfig.withLocalAddress(...)` | `httpClientBuilder.localAddress(...)` | [Not Supported](https://github.com/aws/aws-sdk-java-v2/issues/857) |
226226
| Expect-Continue Enabled | `clientConfig.setUseExpectContinue(...)`<br />`clientConfig.withUseExpectContinue(...)` | `httpClientBuilder.expectContinueEnabled(...)` | Not Supported ([Request Feature](https://github.com/aws/aws-sdk-java-v2/issues/new)) |
227-
| Connection Reaper | `clientConfig.setUseReaper(...)`<br />`clientConfig.withReaper(...)` | `httpClientBuilder.useIdleConnectionReaper(...)` | [Not Supported](https://github.com/aws/aws-sdk-java-v2/issues/856) |
227+
| Connection Reaper | `clientConfig.setUseReaper(...)`<br />`clientConfig.withReaper(...)` | `httpClientBuilder.useIdleConnectionReaper(...)` | `httpClientBuilder.useIdleConnectionReaper(...)` |
228228
| | `AmazonDynamoDBClientBuilder.standard()`<br />`.withClientConfiguration(clientConfiguration)`<br />`.build()` | `DynamoDbClient.builder()`<br />`.httpClientBuilder(httpClientBuilder)`<br />`.build()` | `DynamoDbAsyncClient.builder()`<br />`.httpClientBuilder(httpClientBuilder)`<br />`.build()` |
229229

230230

http-client-spi/src/main/java/software/amazon/awssdk/http/SdkHttpConfigurationOption.java

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,19 @@ public final class SdkHttpConfigurationOption<T> extends AttributeMap.Key<T> {
5353
public static final SdkHttpConfigurationOption<Duration> CONNECTION_ACQUIRE_TIMEOUT =
5454
new SdkHttpConfigurationOption<>("ConnectionAcquireTimeout", Duration.class);
5555

56+
/**
57+
* Timeout after which an idle connection should be closed.
58+
*/
59+
public static final SdkHttpConfigurationOption<Duration> CONNECTION_MAX_IDLE_TIMEOUT =
60+
new SdkHttpConfigurationOption<>("ConnectionMaxIdleTimeout", Duration.class);
61+
62+
/**
63+
* Timeout after which a connection should be closed, regardless of whether it is idle. Zero indicates an infinite amount
64+
* of time.
65+
*/
66+
public static final SdkHttpConfigurationOption<Duration> CONNECTION_TIME_TO_LIVE =
67+
new SdkHttpConfigurationOption<>("ConnectionTimeToLive", Duration.class);
68+
5669
/**
5770
* Maximum number of connections allowed in a connection pool.
5871
*/
@@ -64,6 +77,7 @@ public final class SdkHttpConfigurationOption<T> extends AttributeMap.Key<T> {
6477
*/
6578
public static final SdkHttpConfigurationOption<Protocol> PROTOCOL =
6679
new SdkHttpConfigurationOption<>("Protocol", Protocol.class);
80+
6781
/**
6882
* Maximum number of requests allowed to wait for a connection.
6983
*/
@@ -77,10 +91,19 @@ public final class SdkHttpConfigurationOption<T> extends AttributeMap.Key<T> {
7791
public static final SdkHttpConfigurationOption<Boolean> TRUST_ALL_CERTIFICATES =
7892
new SdkHttpConfigurationOption<>("TrustAllCertificates", Boolean.class);
7993

94+
/**
95+
* Whether idle connection should be removed after the {@link #CONNECTION_MAX_IDLE_TIMEOUT} has passed.
96+
*/
97+
public static final SdkHttpConfigurationOption<Boolean> REAP_IDLE_CONNECTIONS =
98+
new SdkHttpConfigurationOption<>("ReapIdleConnections", Boolean.class);
99+
80100
private static final Duration DEFAULT_SOCKET_READ_TIMEOUT = Duration.ofSeconds(30);
81101
private static final Duration DEFAULT_SOCKET_WRITE_TIMEOUT = Duration.ofSeconds(30);
82102
private static final Duration DEFAULT_CONNECTION_TIMEOUT = Duration.ofSeconds(2);
83103
private static final Duration DEFAULT_CONNECTION_ACQUIRE_TIMEOUT = Duration.ofSeconds(10);
104+
private static final Duration DEFAULT_CONNECTION_MAX_IDLE_TIMEOUT = Duration.ofSeconds(60);
105+
private static final Duration DEFAULT_CONNECTION_TIME_TO_LIVE = Duration.ZERO;
106+
private static final Boolean DEFAULT_REAP_IDLE_CONNECTIONS = Boolean.TRUE;
84107
private static final int DEFAULT_MAX_CONNECTIONS = 50;
85108
private static final int DEFAULT_MAX_CONNECTION_ACQUIRES = 10_000;
86109
private static final Boolean DEFAULT_TRUST_ALL_CERTIFICATES = Boolean.FALSE;
@@ -93,10 +116,13 @@ public final class SdkHttpConfigurationOption<T> extends AttributeMap.Key<T> {
93116
.put(WRITE_TIMEOUT, DEFAULT_SOCKET_WRITE_TIMEOUT)
94117
.put(CONNECTION_TIMEOUT, DEFAULT_CONNECTION_TIMEOUT)
95118
.put(CONNECTION_ACQUIRE_TIMEOUT, DEFAULT_CONNECTION_ACQUIRE_TIMEOUT)
119+
.put(CONNECTION_MAX_IDLE_TIMEOUT, DEFAULT_CONNECTION_MAX_IDLE_TIMEOUT)
120+
.put(CONNECTION_TIME_TO_LIVE, DEFAULT_CONNECTION_TIME_TO_LIVE)
96121
.put(MAX_CONNECTIONS, DEFAULT_MAX_CONNECTIONS)
97122
.put(MAX_PENDING_CONNECTION_ACQUIRES, DEFAULT_MAX_CONNECTION_ACQUIRES)
98123
.put(PROTOCOL, DEFAULT_PROTOCOL)
99124
.put(TRUST_ALL_CERTIFICATES, DEFAULT_TRUST_ALL_CERTIFICATES)
125+
.put(REAP_IDLE_CONNECTIONS, DEFAULT_REAP_IDLE_CONNECTIONS)
100126
.build();
101127

102128
private final String name;

http-clients/apache-client/src/main/java/software/amazon/awssdk/http/apache/ApacheHttpClient.java

Lines changed: 10 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,9 @@
1919
import static java.util.stream.Collectors.mapping;
2020
import static java.util.stream.Collectors.toList;
2121
import static software.amazon.awssdk.http.SdkHttpConfigurationOption.CONNECTION_ACQUIRE_TIMEOUT;
22+
import static software.amazon.awssdk.http.SdkHttpConfigurationOption.CONNECTION_MAX_IDLE_TIMEOUT;
2223
import static software.amazon.awssdk.http.SdkHttpConfigurationOption.CONNECTION_TIMEOUT;
24+
import static software.amazon.awssdk.http.SdkHttpConfigurationOption.CONNECTION_TIME_TO_LIVE;
2325
import static software.amazon.awssdk.http.SdkHttpConfigurationOption.GLOBAL_HTTP_DEFAULTS;
2426
import static software.amazon.awssdk.http.SdkHttpConfigurationOption.MAX_CONNECTIONS;
2527
import static software.amazon.awssdk.http.SdkHttpConfigurationOption.READ_TIMEOUT;
@@ -134,7 +136,7 @@ private ConnectionManagerAwareHttpClient createClient(ApacheHttpClient.DefaultBu
134136
builder.setRequestExecutor(new HttpRequestExecutor())
135137
// SDK handles decompression
136138
.disableContentCompression()
137-
.setKeepAliveStrategy(buildKeepAliveStrategy(configuration))
139+
.setKeepAliveStrategy(buildKeepAliveStrategy(standardOptions))
138140
.disableRedirectHandling()
139141
.disableAutomaticRetries()
140142
.setUserAgent("") // SDK will set the user agent header in the pipeline. Don't let Apache waste time
@@ -144,7 +146,7 @@ private ConnectionManagerAwareHttpClient createClient(ApacheHttpClient.DefaultBu
144146

145147
if (useIdleConnectionReaper(configuration)) {
146148
IdleConnectionReaper.getInstance().registerConnectionManager(
147-
cm, connectionMaxIdleTime(configuration).toMillis());
149+
cm, resolvedOptions.get(SdkHttpConfigurationOption.CONNECTION_MAX_IDLE_TIMEOUT).toMillis());
148150
}
149151

150152
return new ApacheSdkHttpClient(builder.build(), cm);
@@ -167,16 +169,11 @@ private void addProxyConfig(HttpClientBuilder builder,
167169
}
168170
}
169171

170-
private ConnectionKeepAliveStrategy buildKeepAliveStrategy(ApacheHttpClient.DefaultBuilder configuration) {
171-
long maxIdle = connectionMaxIdleTime(configuration).toMillis();
172+
private ConnectionKeepAliveStrategy buildKeepAliveStrategy(AttributeMap standardOptions) {
173+
long maxIdle = standardOptions.get(SdkHttpConfigurationOption.CONNECTION_MAX_IDLE_TIMEOUT).toMillis();
172174
return maxIdle > 0 ? new SdkConnectionKeepAliveStrategy(maxIdle) : null;
173175
}
174176

175-
private Duration connectionMaxIdleTime(DefaultBuilder configuration) {
176-
return Optional.ofNullable(configuration.connectionMaxIdleTime)
177-
.orElse(DefaultConfiguration.MAX_IDLE_CONNECTION_TIME);
178-
}
179-
180177
private boolean useIdleConnectionReaper(DefaultBuilder configuration) {
181178
return Boolean.TRUE.equals(configuration.useIdleConnectionReaper);
182179
}
@@ -336,7 +333,7 @@ public interface Builder extends SdkHttpClient.Builder<ApacheHttpClient.Builder>
336333
* Configure whether the idle connections in the connection pool should be closed asynchronously.
337334
* <p>
338335
* When enabled, connections left idling for longer than {@link #connectionMaxIdleTime(Duration)} will be
339-
* closed. If no value is set, the default value of {@link DefaultConfiguration#MAX_IDLE_CONNECTION_TIME} is used.
336+
* closed. This will not close connections currently in use. By default, this is enabled.
340337
*/
341338
Builder useIdleConnectionReaper(Boolean useConnectionReaper);
342339
}
@@ -346,8 +343,6 @@ private static final class DefaultBuilder implements Builder {
346343
private ProxyConfiguration proxyConfiguration = ProxyConfiguration.builder().build();
347344
private InetAddress localAddress;
348345
private Boolean expectContinueEnabled;
349-
private Duration connectionTimeToLive;
350-
private Duration connectionMaxIdleTime;
351346
private Boolean useIdleConnectionReaper;
352347

353348
private DefaultBuilder() {
@@ -431,7 +426,7 @@ public void setExpectContinueEnabled(Boolean useExpectContinue) {
431426

432427
@Override
433428
public Builder connectionTimeToLive(Duration connectionTimeToLive) {
434-
this.connectionTimeToLive = connectionTimeToLive;
429+
standardOptions.put(CONNECTION_TIME_TO_LIVE, connectionTimeToLive);
435430
return this;
436431
}
437432

@@ -441,7 +436,7 @@ public void setConnectionTimeToLive(Duration connectionTimeToLive) {
441436

442437
@Override
443438
public Builder connectionMaxIdleTime(Duration maxIdleConnectionTimeout) {
444-
this.connectionMaxIdleTime = maxIdleConnectionTimeout;
439+
standardOptions.put(CONNECTION_MAX_IDLE_TIMEOUT, maxIdleConnectionTimeout);
445440
return this;
446441
}
447442

@@ -478,9 +473,7 @@ public HttpClientConnectionManager create(ApacheHttpClient.DefaultBuilder config
478473
null,
479474
DefaultSchemePortResolver.INSTANCE,
480475
null,
481-
Optional.ofNullable(configuration.connectionTimeToLive)
482-
.orElse(DefaultConfiguration.CONNECTION_POOL_TTL)
483-
.toMillis(),
476+
standardOptions.get(SdkHttpConfigurationOption.CONNECTION_TIME_TO_LIVE).toMillis(),
484477
TimeUnit.MILLISECONDS);
485478

486479
cm.setDefaultMaxPerRoute(standardOptions.get(SdkHttpConfigurationOption.MAX_CONNECTIONS));

http-clients/apache-client/src/main/java/software/amazon/awssdk/http/apache/internal/DefaultConfiguration.java

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -15,27 +15,13 @@
1515

1616
package software.amazon.awssdk.http.apache.internal;
1717

18-
import java.time.Duration;
1918
import software.amazon.awssdk.annotations.SdkInternalApi;
2019

2120
/**
2221
* Default configuration values.
2322
*/
2423
@SdkInternalApi
2524
public final class DefaultConfiguration {
26-
27-
/**
28-
* The default maximum idle time (in milliseconds) for a connection to be idle in the connection pool and
29-
* still be eligible for reuse.
30-
*/
31-
public static final Duration MAX_IDLE_CONNECTION_TIME = Duration.ofSeconds(60);
32-
33-
/**
34-
* The default expiration time for a connection in the connection pool.
35-
* A value of -1 means infinite TTL in Apache.
36-
*/
37-
public static final Duration CONNECTION_POOL_TTL = Duration.ofMillis(-1);
38-
3925
public static final Boolean EXPECT_CONTINUE_ENABLED = Boolean.TRUE;
4026

4127
private DefaultConfiguration() {

0 commit comments

Comments
 (0)