使用metrics监控Feign的调用

我们公司的微服务去年已经开始使用metrics来收集应用运行指标,结合prometheus和grafana来统一收集和可视化。服务间的调用我们是使用Feign来进行调用的,但是发现springboot应用没法收集Feign调用得metrics信息,特此,记录一下怎么收集Feign调用的metrics信息。

首先我们来看一下springboot2.X默认使用的metrics指标工具micrometer,查看它的官方文档,我们发现它对okhttp做了支持,如下:

既然如此,那么我们可以调整Feign的默认底层调用实现,使其使用okhttp来调用,从而解决Feign的metrics指标问题。

在此,我们需要注意的是 OkHttpClient Metrics 使用的okhttp的 eventListener 功能,这个功能在早期的okhttp没有提供。我查看了一下okhttp文档,发现如下:

Version 3.11.0
...
New: The EventListener API previewed in OkHttp 3.9 has graduated to a stable API. Use this interface to track metrics and monitor HTTP requests’ size and duration.

表明,在3.11.0版本,该功能已经稳定。所以我们需要的版本>=3.11.0
下面,开始演示。

项目搭建

1. pom.xml

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.1.6.RELEASE</version>
    <relativePath/> <!-- lookup parent from repository -->
</parent>
<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-openfeign</artifactId>
    </dependency>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>
    <dependency>
        <groupId>io.github.openfeign</groupId>
        <artifactId>feign-okhttp</artifactId>
        <version>10.7.4</version>
        <exclusions>
            <exclusion>
                <artifactId>okhttp</artifactId>
                <groupId>com.squareup.okhttp3</groupId>
            </exclusion>
        </exclusions>
    </dependency>
    <dependency>
        <groupId>com.squareup.okhttp3</groupId>
        <artifactId>okhttp</artifactId>
        <version>3.14.4</version>
    </dependency>
    <dependency>
        <groupId>io.micrometer</groupId>
        <artifactId>micrometer-registry-prometheus</artifactId>
        <version>1.2.0</version>
    </dependency>
</dependencies>

2. Feign底层使用okhttp

配置文件

feign:
httpclient:
  enabled: false
okhttp:
  enabled: true

springboot配置

我们可以直接将FeignAutoConfiguration里面的okhttp配置拿出来

@Configuration
public class FeignConfig {
    @Configuration
    @ConditionalOnClass(OkHttpClient.class)
    @ConditionalOnMissingClass("com.netflix.loadbalancer.ILoadBalancer")
    @ConditionalOnProperty("feign.okhttp.enabled")
    protected static class OkHttpFeignConfiguration {
        private okhttp3.OkHttpClient okHttpClient;
        @Bean
        @ConditionalOnMissingBean(ConnectionPool.class)
        public ConnectionPool httpClientConnectionPool(
                FeignHttpClientProperties httpClientProperties,
                OkHttpClientConnectionPoolFactory connectionPoolFactory) {
            int maxTotalConnections = httpClientProperties.getMaxConnections();
            long timeToLive = httpClientProperties.getTimeToLive();
            TimeUnit ttlUnit = httpClientProperties.getTimeToLiveUnit();
            return connectionPoolFactory.create(maxTotalConnections, timeToLive, ttlUnit);
        }
        @Bean
        public okhttp3.OkHttpClient client(OkHttpClientFactory httpClientFactory,
                                           ConnectionPool connectionPool,
                                           FeignHttpClientProperties httpClientProperties,
                                           OkHttpMetricsEventListener okHttpMetricsEventListener) {
            this.okHttpClient = httpClientFactory.createBuilder(httpClientProperties.isDisableSslValidation())
                    .eventListener(okHttpMetricsEventListener) // 配置metrics监听器
                    .connectTimeout(httpClientProperties.getConnectionTimeout(), TimeUnit.MILLISECONDS)
                    .followRedirects(httpClientProperties.isFollowRedirects())
                    .connectionPool(connectionPool)
                    .build();
            return this.okHttpClient;
        }
        @PreDestroy
        public void destroy() {
            if (this.okHttpClient != null) {
                this.okHttpClient.dispatcher().executorService().shutdown();
                this.okHttpClient.connectionPool().evictAll();
            }
        }
        @Bean
        public OkHttpMetricsEventListener okHttpMetricsEventListener(MeterRegistry meterRegistry) {
            return  OkHttpMetricsEventListener.builder(meterRegistry, "ok")
                    .uriMapper(request -> request.url().encodedPath()) // 默认也支持URI_PATTERN header支持uri tag
                    .tags(Collections.emptyList()) // 这里可以指定自定义的tag
                    .build();
        }
        @Bean
        @ConditionalOnMissingBean(Client.class)
        public Client feignClient(okhttp3.OkHttpClient client) {
            return new OkHttpClient(client);
        }
    }
}

主要是增加了

...
.eventListener(okHttpMetricsEventListener) // 配置metrics监听器
...
@Bean
public OkHttpMetricsEventListener okHttpMetricsEventListener(MeterRegistry meterRegistry) {
      return  OkHttpMetricsEventListener.builder(meterRegistry, "ok")
              .uriMapper(request -> request.url().encodedPath())
              .tags(Collections.emptyList())
              .build();
  } 

3. Feign请求代码

@FeignClient(name = "test", url = "http://localhost:8080")
public interface TestClient {
    @GetMapping("/api/test")
    BaseResp<String> test();
}
@RestController
public class FeignTestController {
    @Autowired
    private TestClient testClient;
    @GetMapping("/test")
    public String test() {
        BaseResp<String> test = testClient.test();
        System.out.println(test);
        return "ok";
    }
}

然后通过web暴露出这个接口给外部调用。

4. 观察metrics信息

http://localhost:8080/actuator/prometheus

如下,是一段相关的信息

# HELP ok_seconds Timer of OkHttp operation
# TYPE ok_seconds summary
ok_seconds_count{host="localhost",method="GET",status="200",uri="/api/test",} 8.0
ok_seconds_sum{host="localhost",method="GET",status="200",uri="/api/test",} 0.109059855
# HELP ok_seconds_max Timer of OkHttp operation
# TYPE ok_seconds_max gauge
ok_seconds_max{host="localhost",method="GET",status="200",uri="/api/test",} 0.004678218

到此,我们就可以获取到相关的Feign调用信息,然后就可以进行相关的监控和可视化展示。


原文:https://justsme.github.io/2020/02/22/监控Feign调用metrics/
作者:justsme

当前作者使用的是springboot 2.1.6版本,对应的feign确实不支持收集metrics指标。对于作者收集okhttp的指标有以下几点疑问:
1.该指标是收集到okhttp的,对于存在下游服务未注册的情况下,feign会报错,走不到okhhtp这里,这时就收集不到这种报错场景了
2.这个方案支持不到feign底层HttpUrlConnnetion和apache HttpClient的场景。不能达到通用

当前新版本的springboot 2.5.3是支持feign metric收集的,需要依赖如下jar包

io.github.openfeign
feign-micrometer

1 个赞