1. 概述
在預設情況下 spring cloud feign在進行各個子服務之間的呼叫時,http元件使用的是jdk的HttpURLConnection,沒有使用執行緒池。本文先從原始碼分析feign的http元件物件生成的過程,然後通過為feign配置http執行緒池優化呼叫效率。
2. 原始碼分析
我們分析原始碼spring cloud feign。在spring-cloud-netflix-core/META-INF/spring.factories中可以看到,在spring boot自動配置會初始化FeignRibbonClientAutoConfiguration,這個類會生成Ribbon的使用http元件。
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.cloud.netflix.feign.ribbon.FeignRibbonClientAutoConfiguration,\
複製程式碼
分析配置類是FeignRibbonClientAutoConfiguration 下面分析此類import的3個類:HttpClientFeignLoadBalancedConfiguration,OkHttpFeignLoadBalancedConfiguration,DefaultFeignLoadBalancedConfiguration
@Import({ HttpClientFeignLoadBalancedConfiguration.class,
OkHttpFeignLoadBalancedConfiguration.class,
DefaultFeignLoadBalancedConfiguration.class })
public class FeignRibbonClientAutoConfiguration {
…
}
複製程式碼
HttpClientFeignLoadBalancedConfiguration 為feigin配置appache client的執行緒池 當引入ApacheHttpClient.class類時,會初始化這個配置類 方法feignClient()中:根據@ConditionalOnMissingBean(Client.class)知道如果有HttpClient 物件,則建立的ApacheHttpClient使用自己定義的HttpClient 。如果沒有,則使用預設值。最後生成LoadBalancerFeignClient物件
@Configuration
@ConditionalOnClass(ApacheHttpClient.class)
@ConditionalOnProperty(value = "feign.httpclient.enabled", matchIfMissing = true)
class HttpClientFeignLoadBalancedConfiguration {
@Autowired(required = false)
private HttpClient httpClient;
@Bean
@ConditionalOnMissingBean(Client.class)
public Client feignClient(CachingSpringLoadBalancerFactory cachingFactory,
SpringClientFactory clientFactory) {
ApacheHttpClient delegate;
if (this.httpClient != null) {
delegate = new ApacheHttpClient(this.httpClient);
} else {
delegate = new ApacheHttpClient();
}
return new LoadBalancerFeignClient(delegate, cachingFactory, clientFactory);
}
}
複製程式碼
OkHttpFeignLoadBalancedConfiguration 為feigin配置OkHttp,類似apache httpclient, 這裡略。 DefaultFeignLoadBalancedConfiguration 為feigin配置HttpURLConnection, 方法feignClient():只有以上兩個Client沒有生產物件時,才在這個方法中使用Client.Default生成LoadBalancerFeignClient
@Configuration
class DefaultFeignLoadBalancedConfiguration {
@Bean
@ConditionalOnMissingBean
public Client feignClient(CachingSpringLoadBalancerFactory cachingFactory,
SpringClientFactory clientFactory) {
return new LoadBalancerFeignClient(new Client.Default(null, null),
cachingFactory, clientFactory);
}
}
複製程式碼
檢視Client.Default的原始碼,Default 使用HttpURLConnection 建立連線且每次請求都建立一個新的連線
public static class Default implements Client {
@Override
public Response execute(Request request, Options options) throws IOException {
HttpURLConnection connection = convertAndSend(request, options);
return convertResponse(connection).toBuilder().request(request).build();
}
….
}
複製程式碼
綜上所述,在預設情況下,spring cloud 沒有引入httpclient和okhttp的jar包,所有預設使用HttpURLConnection
3. 使用appach httpclient執行緒池
預設情況下,服務之間呼叫使用的HttpURLConnection,效率非常低。為了提高效率,可以通過連線池提高效率,本節我們使用appache httpclient做為連線池。配置OkHttpClient連線池,也是類似的方法,這裡略。 經過上節的分析,配置執行緒池方法:引入appache httpclient並啟動對應配置,最後還需要生成HttpClient物件。
3.1. pom.xml中引入feign-httpclient.jar
<!-- 增加feign-httpclient -->
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-httpclient</artifactId>
</dependency>
複製程式碼
3.2. 配置引數application-hystrix-feign.yml啟動httpclient
# feign配置
feign:
hystrix:
# 在feign中開啟hystrix功能,預設情況下feign不開啟hystrix功能
enabled: true
## 配置httpclient執行緒池
httpclient:
enabled: true
okhttp:
enabled: false
複製程式碼
3.3. 自定義配置類
使用配置類,生成HttpClient 物件。因為使用PoolingHttpClientConnectionManager連線池,我們需要啟動定時器,定時回收過期的連線。配置定時回收連線池的原因,見問題備忘: httpclient連線池異常引發的慘案
@Configuration
public class HttpPool {
@Bean
public HttpClient httpClient(){
System.out.println("init feign httpclient configuration " );
// 生成預設請求配置
RequestConfig.Builder requestConfigBuilder = RequestConfig.custom();
// 超時時間
requestConfigBuilder.setSocketTimeout(5 * 1000);
// 連線時間
requestConfigBuilder.setConnectTimeout(5 * 1000);
RequestConfig defaultRequestConfig = requestConfigBuilder.build();
// 連線池配置
// 長連線保持30秒
final PoolingHttpClientConnectionManager pollingConnectionManager = new PoolingHttpClientConnectionManager(30, TimeUnit.MILLISECONDS);
// 總連線數
pollingConnectionManager.setMaxTotal(5000);
// 同路由的併發數
pollingConnectionManager.setDefaultMaxPerRoute(100);
// httpclient 配置
HttpClientBuilder httpClientBuilder = HttpClientBuilder.create();
// 保持長連線配置,需要在頭新增Keep-Alive
httpClientBuilder.setKeepAliveStrategy(new DefaultConnectionKeepAliveStrategy());
httpClientBuilder.setConnectionManager(pollingConnectionManager);
httpClientBuilder.setDefaultRequestConfig(defaultRequestConfig);
HttpClient client = httpClientBuilder.build();
// 啟動定時器,定時回收過期的連線
Timer timer = new Timer();
timer.schedule(new TimerTask() {
@Override
public void run() {
// System.out.println("=====closeIdleConnections===");
pollingConnectionManager.closeExpiredConnections();
pollingConnectionManager.closeIdleConnections(5, TimeUnit.SECONDS);
}
}, 10 * 1000, 5 * 1000);
System.out.println("===== Apache httpclient 初始化連線池===");
return client;
}
}
複製程式碼
3.4. 測試:
啟動工程:cloud-registration-center、cloud-service-hystrix 啟動服務:HystrixFeignCloudConsumerApplication 執行請求:http://127.0.0.1:12082/hystrix-feign/simple
配置日誌為debug輸出(設定logback-spring.xml為 level為DEBUG),如果日誌有類似一下的輸出(包含PoolingHttpClientConnectionManager ),則表示連線池配置成功
2018-04-09 23:11:49.017 [hystrix-cloud-hystrix-service-1] DEBUG o.a.h.i.c.PoolingHttpClientConnectionManager - Connection request: [route: {}->http://192.168.0.113:12081][total kept alive: 0; route allocated: 0 of 100; total allocated: 0 of 5000]
2018-04-09 23:11:49.020 [hystrix-cloud-hystrix-service-1] DEBUG o.a.h.i.c.PoolingHttpClientConnectionManager - Connection leased: [id: 0][route: {}->http://192.168.0.113:12081][total kept alive: 0; route allocated: 1 of 100; total allocated: 1 of 5000]
複製程式碼
4. 程式碼
以上的詳細的程式碼見下面 github程式碼,請儘量使用tag v0.12,不要使用master,因為我不能保證master程式碼一直不變