Skip to content

Commit

Permalink
Support metrics for Apache http components async client pools (#1716)
Browse files Browse the repository at this point in the history
The existing PoolingHttpClientConnectionManagerMetricsBinder only supported the synchronous client's connection manager. This updates it to support the asynchronous client's connection manager as well.
  • Loading branch information
worldtiki authored and shakuzen committed Jan 3, 2020
1 parent e887de0 commit b5dd782
Show file tree
Hide file tree
Showing 3 changed files with 58 additions and 54 deletions.
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

0 comments on commit b5dd782

Please sign in to comment.