Skip to content
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

Support metrics for Apache http components async client pools #1716

Merged
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
1 change: 1 addition & 0 deletions micrometer-core/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ dependencies {

// apache httpcomponents monitoring
compile 'org.apache.httpcomponents:httpclient:4.5.6', optional
compile 'org.apache.httpcomponents:httpasyncclient:4.1.4', optional

// hystrix monitoring
// metrics are better with https://github.com/Netflix/Hystrix/pull/1568 introduced
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,49 +15,52 @@
*/
package io.micrometer.core.instrument.binder.httpcomponents;

import io.micrometer.core.instrument.*;
import io.micrometer.core.instrument.Gauge;
import io.micrometer.core.instrument.MeterRegistry;
import io.micrometer.core.instrument.Tag;
import io.micrometer.core.instrument.Tags;
import io.micrometer.core.instrument.binder.MeterBinder;
import io.micrometer.core.lang.NonNull;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.conn.routing.HttpRoute;
import org.apache.http.pool.ConnPoolControl;

/**
* Collects metrics from a {@link PoolingHttpClientConnectionManager}.
*
* Collects metrics from a {@link ConnPoolControl}, for example a {@link org.apache.http.impl.conn.PoolingHttpClientConnectionManager}.
* <p>
* It monitors the overall connection pool state.
*
* @author Benjamin Hubert (benjamin.hubert@willhaben.at)
* @since 1.3.0
*/
public class PoolingHttpClientConnectionManagerMetricsBinder implements MeterBinder {

private final PoolingHttpClientConnectionManager connectionManager;
private final ConnPoolControl<HttpRoute> connPoolControl;
private final Iterable<Tag> tags;

/**
* Creates a metrics binder for the given pooling connection manager.
* Creates a metrics binder for the given pooling connection pool control.
*
* @param connectionManager The connection manager to monitor.
* @param name Name of the connection manager. Will be added as tag with the
* key "httpclient".
* @param tags Tags to apply to all recorded metrics. Must be an even number
* of arguments representing key/value pairs of tags.
* @param connPoolControl The connection pool control to monitor.
* @param name Name of the connection pool control. Will be added as tag with the
* key "httpclient".
* @param tags Tags to apply to all recorded metrics. Must be an even number
* of arguments representing key/value pairs of tags.
*/
@SuppressWarnings("WeakerAccess")
public PoolingHttpClientConnectionManagerMetricsBinder(PoolingHttpClientConnectionManager connectionManager, String name, String... tags) {
this(connectionManager, name, Tags.of(tags));
public PoolingHttpClientConnectionManagerMetricsBinder(ConnPoolControl<HttpRoute> connPoolControl, String name, String... tags) {
this(connPoolControl, name, Tags.of(tags));
}

/**
* Creates a metrics binder for the given pooling connection manager.
* Creates a metrics binder for the given connection pool control.
*
* @param connectionManager The connection manager to monitor.
* @param name Name of the connection manager. Will be added as tag with the
* key "httpclient".
* @param tags Tags to apply to all recorded metrics.
* @param connPoolControl The connection pool control to monitor.
* @param name Name of the connection pool control. Will be added as tag with the key "httpclient".
* @param tags Tags to apply to all recorded metrics.
*/
@SuppressWarnings("WeakerAccess")
public PoolingHttpClientConnectionManagerMetricsBinder(PoolingHttpClientConnectionManager connectionManager, String name, Iterable<Tag> tags) {
this.connectionManager = connectionManager;
public PoolingHttpClientConnectionManagerMetricsBinder(ConnPoolControl<HttpRoute> connPoolControl, String name, Iterable<Tag> tags) {
this.connPoolControl = connPoolControl;
this.tags = Tags.concat(tags, "httpclient", name);
}

Expand All @@ -68,35 +71,35 @@ public void bindTo(@NonNull MeterRegistry registry) {

private void registerTotalMetrics(MeterRegistry registry) {
Gauge.builder("httpcomponents.httpclient.pool.total.max",
connectionManager,
(connectionManager) -> connectionManager.getTotalStats().getMax())
.description("The configured maximum number of allowed persistent connections for all routes.")
.tags(tags)
.register(registry);
connPoolControl,
(connPoolControl) -> connPoolControl.getTotalStats().getMax())
.description("The configured maximum number of allowed persistent connections for all routes.")
.tags(tags)
.register(registry);
Gauge.builder("httpcomponents.httpclient.pool.total.connections",
connectionManager,
(connectionManager) -> connectionManager.getTotalStats().getAvailable())
.description("The number of persistent and available connections for all routes.")
.tags(tags).tag("state", "available")
.register(registry);
connPoolControl,
(connPoolControl) -> connPoolControl.getTotalStats().getAvailable())
.description("The number of persistent and available connections for all routes.")
.tags(tags).tag("state", "available")
.register(registry);
Gauge.builder("httpcomponents.httpclient.pool.total.connections",
connectionManager,
(connectionManager) -> connectionManager.getTotalStats().getLeased())
.description("The number of persistent and leased connections for all routes.")
.tags(tags).tag("state", "leased")
.register(registry);
connPoolControl,
(connPoolControl) -> connPoolControl.getTotalStats().getLeased())
.description("The number of persistent and leased connections for all routes.")
.tags(tags).tag("state", "leased")
.register(registry);
Gauge.builder("httpcomponents.httpclient.pool.total.pending",
connectionManager,
(connectionManager) -> connectionManager.getTotalStats().getPending())
.description("The number of connection requests being blocked awaiting a free connection for all routes.")
.tags(tags)
.register(registry);
connPoolControl,
(connPoolControl) -> connPoolControl.getTotalStats().getPending())
.description("The number of connection requests being blocked awaiting a free connection for all routes.")
.tags(tags)
.register(registry);
Gauge.builder("httpcomponents.httpclient.pool.route.max.default",
connectionManager,
PoolingHttpClientConnectionManager::getDefaultMaxPerRoute)
.description("The configured default maximum number of allowed persistent connections per route.")
.tags(tags)
.register(registry);
connPoolControl,
ConnPoolControl::getDefaultMaxPerRoute)
.description("The configured default maximum number of allowed persistent connections per route.")
.tags(tags)
.register(registry);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
import io.micrometer.core.instrument.MockClock;
import io.micrometer.core.instrument.simple.SimpleConfig;
import io.micrometer.core.instrument.simple.SimpleMeterRegistry;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.pool.ConnPoolControl;
import org.apache.http.pool.PoolStats;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
Expand All @@ -36,21 +36,21 @@
class PoolingHttpClientConnectionManagerMetricsBinderTest {

private MeterRegistry registry = new SimpleMeterRegistry(SimpleConfig.DEFAULT, new MockClock());
private PoolingHttpClientConnectionManager connectionManager;
private ConnPoolControl connPoolControl;
private PoolingHttpClientConnectionManagerMetricsBinder binder;

@BeforeEach
void setup() {
connectionManager = mock(PoolingHttpClientConnectionManager.class);
binder = new PoolingHttpClientConnectionManagerMetricsBinder(connectionManager, "test");
connPoolControl = mock(ConnPoolControl.class);
binder = new PoolingHttpClientConnectionManagerMetricsBinder(connPoolControl, "test");
binder.bindTo(registry);
}

@Test
void totalMax() {
PoolStats poolStats = mock(PoolStats.class);
when(poolStats.getMax()).thenReturn(13);
when(connectionManager.getTotalStats()).thenReturn(poolStats);
when(connPoolControl.getTotalStats()).thenReturn(poolStats);
assertThat(registry.get("httpcomponents.httpclient.pool.total.max")
.tags("httpclient", "test")
.gauge().value()).isEqualTo(13.0);
Expand All @@ -60,7 +60,7 @@ void totalMax() {
void totalAvailable() {
PoolStats poolStats = mock(PoolStats.class);
when(poolStats.getAvailable()).thenReturn(17);
when(connectionManager.getTotalStats()).thenReturn(poolStats);
when(connPoolControl.getTotalStats()).thenReturn(poolStats);
assertThat(registry.get("httpcomponents.httpclient.pool.total.connections")
.tags("httpclient", "test", "state", "available")
.gauge().value()).isEqualTo(17.0);
Expand All @@ -70,7 +70,7 @@ void totalAvailable() {
void totalLeased() {
PoolStats poolStats = mock(PoolStats.class);
when(poolStats.getLeased()).thenReturn(23);
when(connectionManager.getTotalStats()).thenReturn(poolStats);
when(connPoolControl.getTotalStats()).thenReturn(poolStats);
assertThat(registry.get("httpcomponents.httpclient.pool.total.connections")
.tags("httpclient", "test", "state", "leased")
.gauge().value()).isEqualTo(23.0);
Expand All @@ -80,15 +80,15 @@ void totalLeased() {
void totalPending() {
PoolStats poolStats = mock(PoolStats.class);
when(poolStats.getPending()).thenReturn(37);
when(connectionManager.getTotalStats()).thenReturn(poolStats);
when(connPoolControl.getTotalStats()).thenReturn(poolStats);
assertThat(registry.get("httpcomponents.httpclient.pool.total.pending")
.tags("httpclient", "test")
.gauge().value()).isEqualTo(37.0);
}

@Test
void routeMaxDefault() {
when(connectionManager.getDefaultMaxPerRoute()).thenReturn(7);
when(connPoolControl.getDefaultMaxPerRoute()).thenReturn(7);
assertThat(registry.get("httpcomponents.httpclient.pool.route.max.default")
.tags("httpclient", "test")
.gauge().value()).isEqualTo(7.0);
Expand Down