Skip to content

Add apache HTTP request stats #1863

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
May 28, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions http-client-spi/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,11 @@
<artifactId>utils</artifactId>
<version>${awsjavasdk.version}</version>
</dependency>
<dependency>
<groupId>software.amazon.awssdk</groupId>
<artifactId>metrics-spi</artifactId>
<version>${awsjavasdk.version}</version>
</dependency>
<dependency>
<groupId>org.reactivestreams</groupId>
<artifactId>reactive-streams</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

import java.util.Optional;
import software.amazon.awssdk.annotations.SdkPublicApi;
import software.amazon.awssdk.metrics.MetricCollector;

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

private final SdkHttpRequest request;
private final Optional<ContentStreamProvider> contentStreamProvider;
private final MetricCollector metricCollector;

private HttpExecuteRequest(BuilderImpl builder) {
this.request = builder.request;
this.contentStreamProvider = builder.contentStreamProvider;
this.metricCollector = builder.metricCollector;
}

/**
Expand All @@ -48,6 +51,13 @@ public Optional<ContentStreamProvider> contentStreamProvider() {
return contentStreamProvider;
}

/**
* @return The {@link MetricCollector}.
*/
public Optional<MetricCollector> metricCollector() {
return Optional.ofNullable(metricCollector);
}

public static Builder builder() {
return new BuilderImpl();
}
Expand All @@ -68,12 +78,22 @@ public interface Builder {
*/
Builder contentStreamProvider(ContentStreamProvider contentStreamProvider);

/**
* Set the {@link MetricCollector} to be used by the HTTP client to
* report metrics collected for this request.
*
* @param metricCollector The metric collector.
* @return This bilder for method chaining.
*/
Builder metricCollector(MetricCollector metricCollector);

HttpExecuteRequest build();
}

private static class BuilderImpl implements Builder {
private SdkHttpRequest request;
private Optional<ContentStreamProvider> contentStreamProvider = Optional.empty();
private MetricCollector metricCollector;

@Override
public Builder request(SdkHttpRequest request) {
Expand All @@ -87,6 +107,12 @@ public Builder contentStreamProvider(ContentStreamProvider contentStreamProvider
return this;
}

@Override
public Builder metricCollector(MetricCollector metricCollector) {
this.metricCollector = metricCollector;
return this;
}

@Override
public HttpExecuteRequest build() {
return new HttpExecuteRequest(this);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/*
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://aws.amazon.com/apache2.0
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/

package software.amazon.awssdk.http;

import software.amazon.awssdk.annotations.SdkPublicApi;
import software.amazon.awssdk.metrics.MetricCategory;
import software.amazon.awssdk.metrics.SdkMetric;

/**
* Metrics collected by HTTP clients.
*/
@SdkPublicApi
public final class HttpMetric {
/**
* The name of the HTTP client.
*/
public static final SdkMetric<String> HTTP_CLIENT_NAME = metric("HttpClientName", String.class);

/**
* The maximum number of connections that will be pooled by the HTTP client.
*/
public static final SdkMetric<Integer> MAX_CONNECTIONS = metric("MaxConnections", Integer.class);

/**
* The number of idle connections in the connection pool that are ready to serve a request.
*/
public static final SdkMetric<Integer> AVAILABLE_CONNECTIONS = metric("AvailableConnections", Integer.class);

/**
* The number of connections from the connection pool that are busy serving requests.
*/
public static final SdkMetric<Integer> LEASED_CONNECTIONS = metric("LeasedConnections", Integer.class);

/**
* The number of requests awaiting a free connection from the pool.
*/
public static final SdkMetric<Integer> PENDING_CONNECTION_ACQUIRES = metric("PendingConnectionAcquires", Integer.class);

private HttpMetric() {
}

private static <T> SdkMetric<T> metric(String name, Class<T> clzz) {
return SdkMetric.create(name, clzz, MetricCategory.DEFAULT, MetricCategory.HTTP_CLIENT);
}
}
5 changes: 5 additions & 0 deletions http-clients/apache-client/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,11 @@
<artifactId>http-client-spi</artifactId>
<version>${awsjavasdk.version}</version>
</dependency>
<dependency>
<groupId>software.amazon.awssdk</groupId>
<artifactId>metrics-spi</artifactId>
<version>${awsjavasdk.version}</version>
</dependency>
<dependency>
<groupId>software.amazon.awssdk</groupId>
<artifactId>utils</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,11 @@
import static java.util.stream.Collectors.groupingBy;
import static java.util.stream.Collectors.mapping;
import static java.util.stream.Collectors.toList;
import static software.amazon.awssdk.http.HttpMetric.AVAILABLE_CONNECTIONS;
import static software.amazon.awssdk.http.HttpMetric.HTTP_CLIENT_NAME;
import static software.amazon.awssdk.http.HttpMetric.LEASED_CONNECTIONS;
import static software.amazon.awssdk.http.HttpMetric.MAX_CONNECTIONS;
import static software.amazon.awssdk.http.HttpMetric.PENDING_CONNECTION_ACQUIRES;
import static software.amazon.awssdk.utils.NumericUtils.saturatedCast;

import java.io.IOException;
Expand Down Expand Up @@ -57,6 +62,7 @@
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.DefaultSchemePortResolver;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.pool.PoolStats;
import org.apache.http.protocol.HttpRequestExecutor;
import software.amazon.awssdk.annotations.SdkPublicApi;
import software.amazon.awssdk.annotations.SdkTestInternalApi;
Expand All @@ -72,6 +78,7 @@
import software.amazon.awssdk.http.TlsTrustManagersProvider;
import software.amazon.awssdk.http.apache.internal.ApacheHttpRequestConfig;
import software.amazon.awssdk.http.apache.internal.DefaultConfiguration;
import software.amazon.awssdk.http.apache.internal.NoOpMetricCollector;
import software.amazon.awssdk.http.apache.internal.SdkProxyRoutePlanner;
import software.amazon.awssdk.http.apache.internal.conn.ClientConnectionManagerFactory;
import software.amazon.awssdk.http.apache.internal.conn.IdleConnectionReaper;
Expand All @@ -81,6 +88,7 @@
import software.amazon.awssdk.http.apache.internal.impl.ApacheSdkHttpClient;
import software.amazon.awssdk.http.apache.internal.impl.ConnectionManagerAwareHttpClient;
import software.amazon.awssdk.http.apache.internal.utils.ApacheUtils;
import software.amazon.awssdk.metrics.MetricCollector;
import software.amazon.awssdk.utils.AttributeMap;
import software.amazon.awssdk.utils.Logger;
import software.amazon.awssdk.utils.Validate;
Expand Down Expand Up @@ -206,11 +214,15 @@ private boolean isProxyEnabled(ProxyConfiguration proxyConfiguration) {

@Override
public ExecutableHttpRequest prepareRequest(HttpExecuteRequest request) {
MetricCollector metricCollector = request.metricCollector().orElseGet(NoOpMetricCollector::create);
metricCollector.reportMetric(HTTP_CLIENT_NAME, clientName());
HttpRequestBase apacheRequest = toApacheRequest(request);
return new ExecutableHttpRequest() {
@Override
public HttpExecuteResponse call() throws IOException {
return execute(apacheRequest);
HttpExecuteResponse executeResponse = execute(apacheRequest);
collectPoolMetric(metricCollector);
return executeResponse;
}

@Override
Expand Down Expand Up @@ -283,6 +295,18 @@ private ApacheHttpRequestConfig createRequestConfig(DefaultBuilder builder,
.build();
}

private void collectPoolMetric(MetricCollector metricCollector) {
HttpClientConnectionManager cm = httpClient.getHttpClientConnectionManager();
if (cm instanceof PoolingHttpClientConnectionManager) {
PoolingHttpClientConnectionManager poolingCm = (PoolingHttpClientConnectionManager) cm;
PoolStats totalStats = poolingCm.getTotalStats();
metricCollector.reportMetric(MAX_CONNECTIONS, totalStats.getMax());
metricCollector.reportMetric(AVAILABLE_CONNECTIONS, totalStats.getAvailable());
metricCollector.reportMetric(LEASED_CONNECTIONS, totalStats.getLeased());
metricCollector.reportMetric(PENDING_CONNECTION_ACQUIRES, totalStats.getPending());
}
}

@Override
public String clientName() {
return CLIENT_NAME;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/*
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://aws.amazon.com/apache2.0
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/

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

import software.amazon.awssdk.annotations.SdkInternalApi;
import software.amazon.awssdk.metrics.MetricCollection;
import software.amazon.awssdk.metrics.MetricCollector;
import software.amazon.awssdk.metrics.SdkMetric;

/**
* A metric collector that doesn't do anything.
*/
@SdkInternalApi
public final class NoOpMetricCollector implements MetricCollector {
private static final NoOpMetricCollector INSTANCE = new NoOpMetricCollector();

@Override
public String name() {
return "NoOp";
}

@Override
public <T> void reportMetric(SdkMetric<T> metric, T data) {
}

@Override
public MetricCollector createChild(String name) {
throw new UnsupportedOperationException("No op collector does not support createChild");
}

@Override
public MetricCollection collect() {
throw new UnsupportedOperationException("No op collector does not support collect");
}

public static NoOpMetricCollector create() {
return INSTANCE;
}
}
Loading