Spring cloud系列十五 使用線程池優化feign的http請求組件

1. 概述

在默認狀況下 spring cloud feign在進行各個子服務之間的調用時,http組件使用的是jdk的HttpURLConnection,沒有使用線程池。本文先從源碼分析feign的http組件對象生成的過程,而後經過爲feign配置http線程池優化調用效率。java

2. 源碼分析

咱們分析源碼spring cloud feign。在spring-cloud-netflix-core/META-INF/spring.factories中能夠看到,在spring boot自動配置會初始化FeignRibbonClientAutoConfiguration,這個類會生成Ribbon的使用http組件。git

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.cloud.netflix.feign.ribbon.FeignRibbonClientAutoConfiguration,\
複製代碼

分析配置類是FeignRibbonClientAutoConfiguration 下面分析此類import的3個類:HttpClientFeignLoadBalancedConfiguration,OkHttpFeignLoadBalancedConfiguration,DefaultFeignLoadBalancedConfigurationgithub

@Import({ HttpClientFeignLoadBalancedConfiguration.class,
	OkHttpFeignLoadBalancedConfiguration.class,
	DefaultFeignLoadBalancedConfiguration.class })
public class FeignRibbonClientAutoConfiguration {
 …
}
複製代碼

HttpClientFeignLoadBalancedConfiguration 爲feigin配置appache client的線程池 當引入ApacheHttpClient.class類時,會初始化這個配置類 方法feignClient()中:根據@ConditionalOnMissingBean(Client.class)知道若是有HttpClient 對象,則建立的ApacheHttpClient使用本身定義的HttpClient 。若是沒有,則使用默認值。最後生成LoadBalancerFeignClient對象spring

@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生成LoadBalancerFeignClientapache

@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 創建鏈接且每次請求都創建一個新的鏈接bash

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對象。app

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鏈接池異常引起的慘案ide

@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代碼一直不變

相關文章
相關標籤/搜索