项目中使用了 feign 作为 http 请求工具 . feign 自带的 http 客户端是 HttpURLConnection , 没有连接池功能 , 可配置能力差 , 因此通常会使用更强大的 OkHttp 作为底层的客户端实现 , 然后很多时候会发现对于 OkHttp 的自定义配置均会失效 , 这里记录下解决方案

先演示下通常是怎么配置的, 首先引入依赖

1
2
3
4
5
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-core</artifactId>
<version>11.8</version>
</dependency>

再修改Feign配置类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
@Configuration
@ConditionalOnClass(Feign.class)
@AutoConfigureBefore(FeignAutoConfiguration.class)
public class FeignConfig {
// 注入feignContract
@Bean
public Contract feignContract() {
return new feign.Contract.Default();
}

// 注入自定义的okHttpClient
@Bean
public okhttp3.OkHttpClient okHttpClient(){
return new okhttp3.OkHttpClient.Builder()
.readTimeout(60, TimeUnit.SECONDS)
.connectTimeout(60, TimeUnit.SECONDS)
.writeTimeout(120, TimeUnit.SECONDS)
.connectionPool(new ConnectionPool())
.build();
}
}

然后开启 OkHttp 作为 Feign 客户端

1
2
3
4
# application.yml
feign:
okhttp:
enabled: true

到这里通常就结束了, 网上许多文章也是如此说明 , 可是当实际需求来时 , 比如请求时增加一个通用 header ,在给这个注入的 Okhttp 增加拦截器时发现实际请求并未生效.

定位问题

可以将服务根日志级别调整为 debug , 然后按照 [文章](真实案例:Feign切换client到okhttp无法生效,被老大骂得有点慌 - 简书) 进行调试 ,

会发现 OkHttp 的配置不符合运行条件 , 因此实际使用的还是默认配置

继续查看 FeignAutoConfiguration 这个配置类的细节

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Configuration
@ConditionalOnClass({Feign.class})
@EnableConfigurationProperties({FeignClientProperties.class, FeignHttpClientProperties.class})
public class FeignAutoConfiguration {
// .....其他的配置
@Configuration
@ConditionalOnClass({OkHttpClient.class})
@ConditionalOnMissingClass({"com.netflix.loadbalancer.ILoadBalancer"})
@ConditionalOnMissingBean({okhttp3.OkHttpClient.class})
@ConditionalOnProperty({"feign.okhttp.enabled"})
protected static class OkHttpFeignConfiguration {
// ...okhttp的配置
}

// .....其他的配置
}

问题就出在这里 @ConditionalOnMissingBean({okhttp3.OkHttpClient.class})

即只有当容器中没有OkHttpClient的实例时 , 才会运行 . 而我们需要的就是注入了自定义 OkHttp , 因此只能自己实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
@Configuration
@ConditionalOnClass(Feign.class)
@AutoConfigureAfter(FeignAutoConfiguration.class)
public class OkHttpFeignConfiguration {

@Bean
@ConditionalOnMissingBean({Client.class})
public Client feignClient(okhttp3.OkHttpClient client) {
return new feign.okhttp.OkHttpClient(client);
}

@Bean
@ConditionalOnMissingBean({ConnectionPool.class})
public ConnectionPool httpClientConnectionPool(FeignHttpClientProperties httpClientProperties, OkHttpClientConnectionPoolFactory connectionPoolFactory) {
Integer maxTotalConnections = httpClientProperties.getMaxConnections();
Long timeToLive = httpClientProperties.getTimeToLive();
TimeUnit ttlUnit = httpClientProperties.getTimeToLiveUnit();
return connectionPoolFactory.create(maxTotalConnections, timeToLive, ttlUnit);
}

@Bean
public OkHttpClient client(OkHttpClientFactory httpClientFactory, ConnectionPool connectionPool, FeignHttpClientProperties httpClientProperties) {
return new OkHttpClient.Builder()
.connectTimeout(8, TimeUnit.SECONDS)
.readTimeout(8,TimeUnit.SECONDS)
.writeTimeout(12,TimeUnit.SECONDS)
.addInterceptor(new HeaderInterceptor())
.sslSocketFactory(SSLSocketClient.getSSLSocketFactory(), new TrustAllManager())
.hostnameVerifier(SSLSocketClient.getHostnameVerifier())
.build();
}
}

当实际运行时 , 我们会发现已经生效 .