Skip to content

Commit 299ed84

Browse files
authored
Merge pull request #1863 from dagnir/apache-metrics
Add apache HTTP request stats
2 parents 5af6d87 + 69e0b3c commit 299ed84

File tree

7 files changed

+300
-1
lines changed

7 files changed

+300
-1
lines changed

http-client-spi/pom.xml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,11 @@
5050
<artifactId>utils</artifactId>
5151
<version>${awsjavasdk.version}</version>
5252
</dependency>
53+
<dependency>
54+
<groupId>software.amazon.awssdk</groupId>
55+
<artifactId>metrics-spi</artifactId>
56+
<version>${awsjavasdk.version}</version>
57+
</dependency>
5358
<dependency>
5459
<groupId>org.reactivestreams</groupId>
5560
<artifactId>reactive-streams</artifactId>

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

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
import java.util.Optional;
1919
import software.amazon.awssdk.annotations.SdkPublicApi;
20+
import software.amazon.awssdk.metrics.MetricCollector;
2021

2122
/**
2223
* Request object containing the parameters necessary to make a synchronous HTTP request.
@@ -28,10 +29,12 @@ public final class HttpExecuteRequest {
2829

2930
private final SdkHttpRequest request;
3031
private final Optional<ContentStreamProvider> contentStreamProvider;
32+
private final MetricCollector metricCollector;
3133

3234
private HttpExecuteRequest(BuilderImpl builder) {
3335
this.request = builder.request;
3436
this.contentStreamProvider = builder.contentStreamProvider;
37+
this.metricCollector = builder.metricCollector;
3538
}
3639

3740
/**
@@ -48,6 +51,13 @@ public Optional<ContentStreamProvider> contentStreamProvider() {
4851
return contentStreamProvider;
4952
}
5053

54+
/**
55+
* @return The {@link MetricCollector}.
56+
*/
57+
public Optional<MetricCollector> metricCollector() {
58+
return Optional.ofNullable(metricCollector);
59+
}
60+
5161
public static Builder builder() {
5262
return new BuilderImpl();
5363
}
@@ -68,12 +78,22 @@ public interface Builder {
6878
*/
6979
Builder contentStreamProvider(ContentStreamProvider contentStreamProvider);
7080

81+
/**
82+
* Set the {@link MetricCollector} to be used by the HTTP client to
83+
* report metrics collected for this request.
84+
*
85+
* @param metricCollector The metric collector.
86+
* @return This bilder for method chaining.
87+
*/
88+
Builder metricCollector(MetricCollector metricCollector);
89+
7190
HttpExecuteRequest build();
7291
}
7392

7493
private static class BuilderImpl implements Builder {
7594
private SdkHttpRequest request;
7695
private Optional<ContentStreamProvider> contentStreamProvider = Optional.empty();
96+
private MetricCollector metricCollector;
7797

7898
@Override
7999
public Builder request(SdkHttpRequest request) {
@@ -87,6 +107,12 @@ public Builder contentStreamProvider(ContentStreamProvider contentStreamProvider
87107
return this;
88108
}
89109

110+
@Override
111+
public Builder metricCollector(MetricCollector metricCollector) {
112+
this.metricCollector = metricCollector;
113+
return this;
114+
}
115+
90116
@Override
91117
public HttpExecuteRequest build() {
92118
return new HttpExecuteRequest(this);
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
/*
2+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License").
5+
* You may not use this file except in compliance with the License.
6+
* A copy of the License is located at
7+
*
8+
* http://aws.amazon.com/apache2.0
9+
*
10+
* or in the "license" file accompanying this file. This file is distributed
11+
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12+
* express or implied. See the License for the specific language governing
13+
* permissions and limitations under the License.
14+
*/
15+
16+
package software.amazon.awssdk.http;
17+
18+
import software.amazon.awssdk.annotations.SdkPublicApi;
19+
import software.amazon.awssdk.metrics.MetricCategory;
20+
import software.amazon.awssdk.metrics.SdkMetric;
21+
22+
/**
23+
* Metrics collected by HTTP clients.
24+
*/
25+
@SdkPublicApi
26+
public final class HttpMetric {
27+
/**
28+
* The name of the HTTP client.
29+
*/
30+
public static final SdkMetric<String> HTTP_CLIENT_NAME = metric("HttpClientName", String.class);
31+
32+
/**
33+
* The maximum number of connections that will be pooled by the HTTP client.
34+
*/
35+
public static final SdkMetric<Integer> MAX_CONNECTIONS = metric("MaxConnections", Integer.class);
36+
37+
/**
38+
* The number of idle connections in the connection pool that are ready to serve a request.
39+
*/
40+
public static final SdkMetric<Integer> AVAILABLE_CONNECTIONS = metric("AvailableConnections", Integer.class);
41+
42+
/**
43+
* The number of connections from the connection pool that are busy serving requests.
44+
*/
45+
public static final SdkMetric<Integer> LEASED_CONNECTIONS = metric("LeasedConnections", Integer.class);
46+
47+
/**
48+
* The number of requests awaiting a free connection from the pool.
49+
*/
50+
public static final SdkMetric<Integer> PENDING_CONNECTION_ACQUIRES = metric("PendingConnectionAcquires", Integer.class);
51+
52+
private HttpMetric() {
53+
}
54+
55+
private static <T> SdkMetric<T> metric(String name, Class<T> clzz) {
56+
return SdkMetric.create(name, clzz, MetricCategory.DEFAULT, MetricCategory.HTTP_CLIENT);
57+
}
58+
}

http-clients/apache-client/pom.xml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,11 @@
3333
<artifactId>http-client-spi</artifactId>
3434
<version>${awsjavasdk.version}</version>
3535
</dependency>
36+
<dependency>
37+
<groupId>software.amazon.awssdk</groupId>
38+
<artifactId>metrics-spi</artifactId>
39+
<version>${awsjavasdk.version}</version>
40+
</dependency>
3641
<dependency>
3742
<groupId>software.amazon.awssdk</groupId>
3843
<artifactId>utils</artifactId>

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

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,11 @@
1818
import static java.util.stream.Collectors.groupingBy;
1919
import static java.util.stream.Collectors.mapping;
2020
import static java.util.stream.Collectors.toList;
21+
import static software.amazon.awssdk.http.HttpMetric.AVAILABLE_CONNECTIONS;
22+
import static software.amazon.awssdk.http.HttpMetric.HTTP_CLIENT_NAME;
23+
import static software.amazon.awssdk.http.HttpMetric.LEASED_CONNECTIONS;
24+
import static software.amazon.awssdk.http.HttpMetric.MAX_CONNECTIONS;
25+
import static software.amazon.awssdk.http.HttpMetric.PENDING_CONNECTION_ACQUIRES;
2126
import static software.amazon.awssdk.utils.NumericUtils.saturatedCast;
2227

2328
import java.io.IOException;
@@ -57,6 +62,7 @@
5762
import org.apache.http.impl.client.HttpClients;
5863
import org.apache.http.impl.conn.DefaultSchemePortResolver;
5964
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
65+
import org.apache.http.pool.PoolStats;
6066
import org.apache.http.protocol.HttpRequestExecutor;
6167
import software.amazon.awssdk.annotations.SdkPublicApi;
6268
import software.amazon.awssdk.annotations.SdkTestInternalApi;
@@ -72,6 +78,7 @@
7278
import software.amazon.awssdk.http.TlsTrustManagersProvider;
7379
import software.amazon.awssdk.http.apache.internal.ApacheHttpRequestConfig;
7480
import software.amazon.awssdk.http.apache.internal.DefaultConfiguration;
81+
import software.amazon.awssdk.http.apache.internal.NoOpMetricCollector;
7582
import software.amazon.awssdk.http.apache.internal.SdkProxyRoutePlanner;
7683
import software.amazon.awssdk.http.apache.internal.conn.ClientConnectionManagerFactory;
7784
import software.amazon.awssdk.http.apache.internal.conn.IdleConnectionReaper;
@@ -81,6 +88,7 @@
8188
import software.amazon.awssdk.http.apache.internal.impl.ApacheSdkHttpClient;
8289
import software.amazon.awssdk.http.apache.internal.impl.ConnectionManagerAwareHttpClient;
8390
import software.amazon.awssdk.http.apache.internal.utils.ApacheUtils;
91+
import software.amazon.awssdk.metrics.MetricCollector;
8492
import software.amazon.awssdk.utils.AttributeMap;
8593
import software.amazon.awssdk.utils.Logger;
8694
import software.amazon.awssdk.utils.Validate;
@@ -206,11 +214,15 @@ private boolean isProxyEnabled(ProxyConfiguration proxyConfiguration) {
206214

207215
@Override
208216
public ExecutableHttpRequest prepareRequest(HttpExecuteRequest request) {
217+
MetricCollector metricCollector = request.metricCollector().orElseGet(NoOpMetricCollector::create);
218+
metricCollector.reportMetric(HTTP_CLIENT_NAME, clientName());
209219
HttpRequestBase apacheRequest = toApacheRequest(request);
210220
return new ExecutableHttpRequest() {
211221
@Override
212222
public HttpExecuteResponse call() throws IOException {
213-
return execute(apacheRequest);
223+
HttpExecuteResponse executeResponse = execute(apacheRequest);
224+
collectPoolMetric(metricCollector);
225+
return executeResponse;
214226
}
215227

216228
@Override
@@ -283,6 +295,18 @@ private ApacheHttpRequestConfig createRequestConfig(DefaultBuilder builder,
283295
.build();
284296
}
285297

298+
private void collectPoolMetric(MetricCollector metricCollector) {
299+
HttpClientConnectionManager cm = httpClient.getHttpClientConnectionManager();
300+
if (cm instanceof PoolingHttpClientConnectionManager) {
301+
PoolingHttpClientConnectionManager poolingCm = (PoolingHttpClientConnectionManager) cm;
302+
PoolStats totalStats = poolingCm.getTotalStats();
303+
metricCollector.reportMetric(MAX_CONNECTIONS, totalStats.getMax());
304+
metricCollector.reportMetric(AVAILABLE_CONNECTIONS, totalStats.getAvailable());
305+
metricCollector.reportMetric(LEASED_CONNECTIONS, totalStats.getLeased());
306+
metricCollector.reportMetric(PENDING_CONNECTION_ACQUIRES, totalStats.getPending());
307+
}
308+
}
309+
286310
@Override
287311
public String clientName() {
288312
return CLIENT_NAME;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
/*
2+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License").
5+
* You may not use this file except in compliance with the License.
6+
* A copy of the License is located at
7+
*
8+
* http://aws.amazon.com/apache2.0
9+
*
10+
* or in the "license" file accompanying this file. This file is distributed
11+
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12+
* express or implied. See the License for the specific language governing
13+
* permissions and limitations under the License.
14+
*/
15+
16+
package software.amazon.awssdk.http.apache.internal;
17+
18+
import software.amazon.awssdk.annotations.SdkInternalApi;
19+
import software.amazon.awssdk.metrics.MetricCollection;
20+
import software.amazon.awssdk.metrics.MetricCollector;
21+
import software.amazon.awssdk.metrics.SdkMetric;
22+
23+
/**
24+
* A metric collector that doesn't do anything.
25+
*/
26+
@SdkInternalApi
27+
public final class NoOpMetricCollector implements MetricCollector {
28+
private static final NoOpMetricCollector INSTANCE = new NoOpMetricCollector();
29+
30+
@Override
31+
public String name() {
32+
return "NoOp";
33+
}
34+
35+
@Override
36+
public <T> void reportMetric(SdkMetric<T> metric, T data) {
37+
}
38+
39+
@Override
40+
public MetricCollector createChild(String name) {
41+
throw new UnsupportedOperationException("No op collector does not support createChild");
42+
}
43+
44+
@Override
45+
public MetricCollection collect() {
46+
throw new UnsupportedOperationException("No op collector does not support collect");
47+
}
48+
49+
public static NoOpMetricCollector create() {
50+
return INSTANCE;
51+
}
52+
}

0 commit comments

Comments
 (0)