在Feign中,Client是一个非常重要的组件,Feign最终发送Request请求以及接收Response响应都是由Client组件来完成的。Client在Feign源码中是一个接口,在默认情况下,Client的实现类是Client.Default。Client.Default是由HttpURLConnection来实现网络请求的。另外,Client还支持HttpClient和OkHttp来进行网络请求。
首先查看FeignRibbonClient的自动配置类FeignRibbonClientAutoConfiguration,该类在程序启动的时候注入一些Bean,其中注入了一个BeanName为feignClient的Client类型的Bean。在省缺配置BeanName为FeignClient的Bean的情况下,会自动注入Client.Default这个对象,跟踪Client.Default源码,Client.Default使用的网络请求框架是HttpURLConnection,代码如下:
-
public
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);
-
}
-
-
......
//代码省略
-
}
这种情况下,由于缺乏连接池的支持,在达到一定流量的后服务肯定会出问题 。
那么如何在Feign中使用HttpClient的框架呢?我们查看FeignAutoConfiguration.HttpClientFeignConfiguration的源码:
-
@Configuration
-
@ConditionalOnClass({ApacheHttpClient.class})
-
@ConditionalOnMissingClass({"com.netflix.loadbalancer.ILoadBalancer"})
-
@ConditionalOnMissingBean({CloseableHttpClient.class})
-
@ConditionalOnProperty(
-
value = {"feign.httpclient.enabled"},
-
matchIfMissing = true
-
)
-
protected static
class HttpClientFeignConfiguration {
-
private
final Timer connectionManagerTimer = new Timer(
"FeignApacheHttpClientConfiguration.connectionManagerTimer",
true);
-
@Autowired(
-
required = false
-
)
-
private RegistryBuilder registryBuilder;
-
private CloseableHttpClient httpClient;
-
-
protected HttpClientFeignConfiguration() {
-
}
-
-
@Bean
-
@ConditionalOnMissingBean({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;
-
}
-
-
@Bean
-
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;
-
}
-
-
@Bean
-
@ConditionalOnMissingBean({Client.class})
-
public Client feignClient(HttpClient httpClient) {
-
return new ApacheHttpClient(httpClient);
-
}
-
-
@PreDestroy
-
public void destroy() throws Exception {
-
this.connectionManagerTimer.cancel();
-
if (
this.httpClient !=
null) {
-
this.httpClient.close();
-
}
-
-
}
-
}
从代码@ConditionalOnClass({ApacheHttpClient.class})注解可知,只需要在pom文件上加上HttpClient依赖即可。另外需要在配置文件中配置feign.httpclient.enabled为true,从@ConditionalOnProperty注解可知,这个配置可以不写,因为在默认情况下就为true:
-
<dependency>
-
<groupId>io.github.openfeign
groupId>
-
<artifactId>feign-httpclient
artifactId>
-
<version>9.4.0
version>
-
dependency>
查看FeignAutoConfiguration.HttpClientFeignConfiguration的源码:
-
@Configuration
-
@ConditionalOnClass({OkHttpClient.class})
-
@ConditionalOnMissingClass({"com.netflix.loadbalancer.ILoadBalancer"})
-
@ConditionalOnMissingBean({okhttp3.OkHttpClient.class})
-
@ConditionalOnProperty({"feign.okhttp.enabled"})
-
protected static
class OkHttpFeignConfiguration {
-
private okhttp3.OkHttpClient okHttpClient;
-
-
protected OkHttpFeignConfiguration() {
-
}
-
-
@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 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;
-
}
-
-
@PreDestroy
-
public void destroy() {
-
if (
this.okHttpClient !=
null) {
-
this.okHttpClient.dispatcher().executorService().shutdown();
-
this.okHttpClient.connectionPool().evictAll();
-
}
-
-
}
-
-
@Bean
-
@ConditionalOnMissingBean({Client.class})
-
public Client feignClient(okhttp3.OkHttpClient client) {
-
return new OkHttpClient(client);
-
}
-
}
同理,如果想要在Feign中使用OkHttp作为网络请求框架,则只需要在pom文件中加上feign-okhttp的依赖,代码如下:
-
<dependency>
-
<groupId>io.github.openfeign
groupId>
-
<artifactId>feign-okhttp
artifactId>
-
<version>10.2.0
version>
-
dependency>