Client组件
在Feign中,Client是一个非常重要的组件,Feign最终发送Request请求以及接收Response响应都是由Client组件来完成的。Client在Feign源码中是一个接口,在默认情况下,Client的实现类是Client.Default
。Client.Default
是由HttpURLConnection
来实现网络请求的。另外,Client还支持HttpClient和OkHttp来进行网络请求。
Client的实现类包括:
Client.Default类:默认的Client实现类,内部使用
HttpURLConnnection
完成HTTP URL请求处理;1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16public static class Default implements Client {
private final SSLSocketFactory sslContextFactory;
private final HostnameVerifier hostnameVerifier;
public Default(SSLSocketFactory sslContextFactory, HostnameVerifier hostnameVerifier) {
this.sslContextFactory = sslContextFactory;
this.hostnameVerifier = hostnameVerifier;
}
public Response execute(Request request, Options options) throws IOException {
HttpURLConnection connection = this.convertAndSend(request, options);
return this.convertResponse(connection, request);
}
......//代码省略
}由于
HttpURLConnnection
由于缺乏连接池的支持,在达到一定流量的后服务可能会出现性能问题。ApacheHttpClient 类:内部使用 Apache Httpclient 开源组件完成HTTP URL请求处理的Client实现类;
- OkHttpClient类:内部使用 OkHttp3 开源组件完成HTTP URL请求处理的Client实现类。
自动配置 FeignAutoConfiguration
OpenFeign的自动配置类:org.springframework.cloud.openfeign.FeignAutoConfiguration
,它配置的最原始的Client。1
2
3
4
5
6
7
8
(Feign.class)
({ FeignClientProperties.class,
FeignHttpClientProperties.class })
public class FeignAutoConfiguration {
//省略...
}
使用HttpClient
自动配置类中,有一个内部类HttpClientFeignConfiguration.class
,它用于配置一个 ApacheHttpClient
客户端。其源码如下: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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
({ApacheHttpClient.class})
"com.netflix.loadbalancer.ILoadBalancer"}) ({
({CloseableHttpClient.class})
(
value = {"feign.httpclient.enabled"},
matchIfMissing = true
)
protected static class HttpClientFeignConfiguration {
private final Timer connectionManagerTimer = new Timer("FeignApacheHttpClientConfiguration.connectionManagerTimer", true);
(
required = false
)
private RegistryBuilder registryBuilder;
private CloseableHttpClient httpClient;
protected HttpClientFeignConfiguration() {
}
({HttpClientConnectionManager.class})
public HttpClientConnectionManager connectionManager(ApacheHttpClientConnectionManagerFactory connectionManagerFactory, FeignHttpClientProperties httpClientProperties) {
final HttpClientConnectionManager connectionManager = connectionManagerFactory.newConnectionManager(httpClientProperties.isDisableSslValidation(), httpClientProperties.getMaxConnections(), httpClientProperties.getMaxConnectionsPerRoute(), httpClientProperties.getTimeToLive(), httpClientProperties.getTimeToLiveUnit(), this.registryBuilder);
this.connectionManagerTimer.schedule(new TimerTask() {
public void run() {
connectionManager.closeExpiredConnections();
}
}, 30000L, (long)httpClientProperties.getConnectionTimerRepeat());
return connectionManager;
}
public CloseableHttpClient httpClient(ApacheHttpClientFactory httpClientFactory, HttpClientConnectionManager httpClientConnectionManager, FeignHttpClientProperties httpClientProperties) {
RequestConfig defaultRequestConfig = RequestConfig.custom().setConnectTimeout(httpClientProperties.getConnectionTimeout()).setRedirectsEnabled(httpClientProperties.isFollowRedirects()).build();
this.httpClient = httpClientFactory.createBuilder().setConnectionManager(httpClientConnectionManager).setDefaultRequestConfig(defaultRequestConfig).build();
return this.httpClient;
}
({Client.class})
public Client feignClient(HttpClient httpClient) {
//Client实例是一个ApacheHttpClient对象
return new ApacheHttpClient(httpClient);
}
public void destroy() throws Exception {
this.connectionManagerTimer.cancel();
if (this.httpClient != null) {
this.httpClient.close();
}
}
}
可以看到这个配置生效的条件是:
@ConditionalOnClass(ApacheHttpClient.class)
: 类路径下存在 ApacheHttpClient 类。@ConditionalOnMissingClass("com.netflix.loadbalancer.ILoadBalancer")
:类路径下不存在 ILoadBalancer 类。feign.httpclient.enabled
的值是true或者缺省(默认是true)。源码如下:
那么如何在Feign中使用HttpClient的框架呢?根据上面FeignAutoConfiguration.HttpClientFeignConfiguration
的源码,可知只需要引入HttpClient的依赖即可,feign.httpclient.enabled
可设置为true或者缺省。1
2
3
4
5<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-httpclient</artifactId>
<version>9.4.0</version>
</dependency>
使用OkHttp
自动配置类中,另外一个内部类 OkHttpFeignConfiguration.class
,它用于配置一个 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
33
34
35
36
({OkHttpClient.class})
"com.netflix.loadbalancer.ILoadBalancer"}) ({
({okhttp3.OkHttpClient.class})
"feign.okhttp.enabled"}) ({
protected static class OkHttpFeignConfiguration {
private okhttp3.OkHttpClient okHttpClient;
protected OkHttpFeignConfiguration() {
}
({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);
}
public okhttp3.OkHttpClient client(OkHttpClientFactory httpClientFactory, ConnectionPool connectionPool, FeignHttpClientProperties httpClientProperties) {
Boolean followRedirects = httpClientProperties.isFollowRedirects();
Integer connectTimeout = httpClientProperties.getConnectionTimeout();
Boolean disableSslValidation = httpClientProperties.isDisableSslValidation();
this.okHttpClient = httpClientFactory.createBuilder(disableSslValidation).connectTimeout((long)connectTimeout, TimeUnit.MILLISECONDS).followRedirects(followRedirects).connectionPool(connectionPool).build();
return this.okHttpClient;
}
({Client.class})
public Client feignClient(okhttp3.OkHttpClient client) {
//Client实例是一个OkHttpClient对象
return new OkHttpClient(client);
}
}
可以看到这个配置生效的条件是:
@ConditionalOnClass(OkHttpClient.class)
: 类路径下存在 OkHttpClient 类。@ConditionalOnMissingClass("com.netflix.loadbalancer.ILoadBalancer")
:类路径下不存在ILoadBalancer
类。feign.okhttp.enabled
的值是true(默认是false)。
所以同理,如果想要在Feign中使用OkHttp作为网络请求框架,则需要在pom文件中加上OkHttp的依赖,并且feign.okhttp.enabled
为ture(这项配置默认是false),代码如下:
pom.xml文件中引入依赖:1
2
3
4
5<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-okhttp</artifactId>
<version>10.2.0</version>
</dependency>
application.yml文件中增加配置项:1
2
3feign:
okhttp:
enabled: true
Ribbon的负载均衡自动配置 FeignRibbonClientAutoConfiguration
除了FeignAutoConfiguration
,还有一个很重要的自动配置类:org.springframework.cloud.openfeign.ribbon.FeignRibbonClientAutoConfiguration
,这个自动配置类位于Robbin的jar包中,它让Client具备负载均衡的能力。
Robbin的依赖:1
2
3
4
5<!-- ribbon-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
</dependency>
下面分析一下这个类的源码:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19import com.netflix.loadbalancer.ILoadBalancer;
//….
({ILoadBalancer.class, Feign.class})
// 本配置类具备优先权 ({FeignAutoConfiguration.class})
({FeignHttpClientProperties.class})
({
HttpClientFeignLoadBalancedConfiguration.class, //配置:包装ApacheHttpClient实例的负载均衡客户端
OkHttpFeignLoadBalancedConfiguration.class, //配置:包装OkHttpClient 实例的负载均衡客户端
DefaultFeignLoadBalancedConfiguration.class //配置:包装Client.Default 实例的负载均衡客户端
})
public class FeignRibbonClientAutoConfiguration {
//空的构造器
public FeignRibbonClientAutoConfiguration() {
}
//...省略
}
根据类上的注解
@AutoConfigureBefore({FeignAutoConfiguration.class})
可知,FeignRibbonClientAutoConfiguration
自动配置是优先于FeignAutoConfiguration
的。所以当包含OpenFeign功能的项目中引入了Robbin之后,FeignRibbonClientAutoConfiguration
自动配置将生效,而FeignAutoConfiguration.class
的自动配置将会被忽略。再根据类上的注解
@ConditionalOnClass({ILoadBalancer.class, Feign.class})
,可知 FeignRibbonClientAutoConfiguration 的自动配置有两个前提条件:- 当前的类路径中,存在
ILoadBalancer.class
类 - 当前的类路径中,存在
Feign.class
类
- 当前的类路径中,存在
结论:当我们引入了Ribbon的依赖,FeignRibbonClientAutoConfiguration 自动配置生效,负载均衡功能也就生效了。
LoadBalancerFeignClient
LoadBalancerFeignClient 类:这是Ribbon jar包中一个特殊的 feign.Client 客户端实现类。内部先使用 Ribbon 负载均衡算法计算server服务器,然后使用包装的 delegate 客户端实例,去完成 HTTP URL请求处理。
FeignRibbonClientAutoConfiguration 自动配置类,并没有直接配置 LoadBalancerFeignClient 容器实例,而是使用@Import注解,通过导入其他配置类的方式,完成 LoadBalancerFeignClient 客户端容器实例的配置。
分别导入了以下三个自动配置类:
- HttpClientFeignLoadBalancedConfiguration.class
该配置类,负责配置一个包装 ApacheHttpClient 实例的 LoadBalancerFeignClient负载均衡客户端。 - OkHttpFeignLoadBalancedConfiguration.class
该配置类,负责配置一个包装 OkHttpClient 实例的 LoadBalancerFeignClient负载均衡客户端。 - DefaultFeignLoadBalancedConfiguration.class
该配置类,负责配置一个包装 Client.Default 实例的 LoadBalancerFeignClient负载均衡客户端。
所以原生的 HttpClientFeignConfiguration 配置变成了 HttpClientFeignLoadBalancedConfiguration:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
(ApacheHttpClient.class)
"feign.httpclient.enabled", matchIfMissing = true) (value =
class HttpClientFeignLoadBalancedConfiguration {
(Client.class)
public Client feignClient(CachingSpringLoadBalancerFactory cachingFactory,
SpringClientFactory clientFactory, HttpClient httpClient) {
ApacheHttpClient delegate = new ApacheHttpClient(httpClient);
return new LoadBalancerFeignClient(delegate, cachingFactory, clientFactory);
}
//省略...
}
原生的 OkHttpFeignConfiguration 配置变成了 OkHttpFeignLoadBalancedConfiguration:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
(OkHttpClient.class)
"feign.okhttp.enabled") (
class OkHttpFeignLoadBalancedConfiguration {
(Client.class)
public Client feignClient(CachingSpringLoadBalancerFactory cachingFactory,
SpringClientFactory clientFactory, okhttp3.OkHttpClient okHttpClient) {
OkHttpClient delegate = new OkHttpClient(okHttpClient);
return new LoadBalancerFeignClient(delegate, cachingFactory, clientFactory);
}
//省略...
}
Client实例变成了LoadBalancerFeignClient,只不过LoadBalancerFeignClient中封装了ApacheHttpClient或者OkHttp。