可观测性-Metrics-Httpclient连接池监控

2023-01-18 21:39:58

前言

老规矩哈,我们先从Micrometer、Spring Boot Actuator、Dropwizard等包中,找找看是否存在相关实现。

micrometer

  • https://micrometer.io
  • https://github.com/micrometer-metrics/micrometer

dropwizard

  • https://metrics.dropwizard.io/4.2.0/index.html#
  • https://github.com/dropwizard/metrics

spring boot actuator

看下面的2个包下代码即可
- org.springframework.boot.actuate.metrics
- org.springframework.boot.actuate.autoconfigure.metrics

由于Spring Boot与 micrometer高度集成,所以优先选择Spring Boot ActuatorMicrometer中的实现。

找到如下几个类

Spring Boot Actuator

  • RestTemplateMetricsConfiguration.java
  • HttpClientMetricsAutoConfiguration.java

Micrometer

  • PoolingHttpClientConnectionManagerMetricsBinder.java
  • MicrometerHttpRequestExecutor.java

首先Httpclient内部已经自带了一些监控指标都在PoolStats对象,
PoolingHttpClientConnectionManager.getTotalStats()方法会返回PoolStats对象,我们只需要把connectionManager传递给PoolingHttpClientConnectionManagerMetricsBinder即可。

new PoolingHttpClientConnectionManagerMetricsBinder(connectionManager, "my-pool")
    .bindTo(registry);

其中PoolStats对象包含以下指标。

  • leased:正在执行请求的连接数。
  • pending:排队获取连接的请求数量。注意:只有当有更多的工作线程争夺更少的连接时,才会发生这种情况。
  • available:当前空闲的连接数。
  • max:允许运行的最大连接数。

池中的连接总数等于available加上leased

PoolingHttpClientConnectionManager(org.apache.http.impl.conn.PoolingHttpClientConnectionManager)是一个HttpClientConnection的连接池,可以为多线程提供并发请求服务。主要是分配连接,回收连接。同一个远程请求,会优先使用连接池提供的空闲的长连接。

监控

添加依赖

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>
 	<dependency>
        <groupId>org.apache.httpcomponents</groupId>
        <artifactId>httpclient</artifactId>
	</dependency>

使用示例

// 1.创建一个PoolingHttpClientConnectionManager
PoolingHttpClientConnectionManager poolingHttpClientConnectionManager = new PoolingHttpClientConnectionManager();
poolingHttpClientConnectionManager.setMaxTotal(200);
poolingHttpClientConnectionManager.setDefaultMaxPerRoute(20);
HttpHost localhost = new HttpHost("locahost", 80);
// 特别指定Route(远程)请求最大的连接数,这里Route - localhost:80最大连接数为50
poolingHttpClientConnectionManager.setMaxPerRoute(new HttpRoute(localhost), 50);
// 2.给创建一个PoolingHttpClientConnectionManager加上监控
new PoolingHttpClientConnectionManagerMetricsBinder(poolingHttpClientConnectionManager, "laker-pool")
        .bindTo(new LoggingMeterRegistry(new LoggingRegistryConfig() {
            @Override
            public String get(String key) {
                return null;
            }
            @Override
            public Duration step() {
                return Duration.ofSeconds(10);
            }
        }, Clock.SYSTEM));
// 3.发起一个请求
CloseableHttpClient httpClient = HttpClientBuilder.create()
        .setConnectionManager(poolingHttpClientConnectionManager)
        // 启动线程,60秒钟从连接池中逐出空闲连接
        .evictIdleConnections(60, TimeUnit.SECONDS)
        .setDefaultRequestConfig(RequestConfig.custom().setConnectionRequestTimeout(2000)
                .setConnectTimeout(2000)
                .setSocketTimeout(2000)
                .build())
        .build();
HttpGet httpGet = new HttpGet("https://www.baidu.com");
//无论请求执行成功还是导致异常,HttpClient都会自动确保将连接释放回连接管理器。
String httpResponse = httpClient.execute(httpGet, response -> EntityUtils.toString(response.getEntity()));
System.out.println(httpResponse);

配置项解释

  • maxTotal:连接池的最大连接数,默认:20
  • defaultMaxPreRount:每个Rount(远程)请求最大的连接数,默认:2
  • setValidateAfterInactivity:连接空闲多长时间(单位:毫秒)进行检查,默认:2000
  • connectTimeout:与服务器连接超时时间,创建socket连接的超时时间。
  • connectionRequestTimeout:从链接池获取连接的超时时间。
  • socketTimeout:socket读取数据的超时时间,从服务器获取数据的超时时间。

结果:

httpcomponents.httpclient.pool.route.max.default{httpclient=laker-pool} value=20
httpcomponents.httpclient.pool.total.connections{httpclient=laker-pool,state=available} value=1
httpcomponents.httpclient.pool.total.connections{httpclient=laker-pool,state=leased} value=0
httpcomponents.httpclient.pool.total.max{httpclient=laker-pool} value=200
httpcomponents.httpclient.pool.total.pending{httpclient=laker-pool} value=0

从指标中可以看出,目前都是针对连接池的监控,没有连接请求耗时的监控,我们稍微等下给加上哈。

池中的连接总数等于available加上leased,即 1 + 0 = 1。

扩展 - 请求耗时统计

Micrometer提供了MicrometerHttpRequestExecutor用于监控请求耗时。

// 加上监控
new PoolingHttpClientConnectionManagerMetricsBinder(poolingHttpClientConnectionManager, "laker-pool")
        .bindTo(loggingMeterRegistry);
CloseableHttpClient httpClient = HttpClientBuilder.create()
        .setConnectionManager(poolingHttpClientConnectionManager)
        // 启动线程,60秒钟从连接池中逐出空闲连接
        .evictIdleConnections(60, TimeUnit.SECONDS)
        .setDefaultRequestConfig(RequestConfig.custom().setConnectionRequestTimeout(2000)
                .setConnectTimeout(2000)
                .setSocketTimeout(2000)
                .build())
    	// 增加
        .setRequestExecutor(MicrometerHttpRequestExecutor
                .builder(loggingMeterRegistry)
                .build())
        .build();

结果

httpcomponents.httpclient.pool.route.max.default{httpclient=laker-pool} value=20
httpcomponents.httpclient.pool.total.connections{httpclient=laker-pool,state=available} value=1
httpcomponents.httpclient.pool.total.connections{httpclient=laker-pool,state=leased} value=0
httpcomponents.httpclient.pool.total.max{httpclient=laker-pool} value=200
httpcomponents.httpclient.pool.total.pending{httpclient=laker-pool} value=0
// 这里哈
httpcomponents.httpclient.request{method=GET,status=200,uri=UNKNOWN} throughput=0.1/s mean=0.1761491s max=0.1761491s
  • 作者:lakernote
  • 原文链接:https://laker.blog.csdn.net/article/details/127167992
    更新时间:2023-01-18 21:39:58