在默認情況下 spring cloud feign在進行各個子服務之間的調用時,http組件使用的是jdk的HttpURLConnection,沒有使用線程池。源碼分析feign的http組件對象生成的過程,然後通過為feign配置http線程池優化調用效率。
我們分析源碼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
<code>@Import({ HttpClientFeignLoadBalancedConfiguration.class, OkHttpFeignLoadBalancedConfiguration.class, DefaultFeignLoadBalancedConfiguration.class })public class FeignRibbonClientAutoConfiguration { …}/<code>
HttpClientFeignLoadBalancedConfiguration為feigin配置appache client的線程池當引入ApacheHttpClient.class類時,會初始化這個配置類方法feignClient()中:根據@ConditionalOnMissingBean(Client.class)知道如果有HttpClient 對象,則創建的ApacheHttpClient使用自己定義的HttpClient 。如果沒有,則使用默認值。最後生成LoadBalancerFeignClient對象
<code>@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); }}/<code>
OkHttpFeignLoadBalancedConfiguration為feigin配置OkHttp,類似apache httpclient, 這裡略。DefaultFeignLoadBalancedConfiguration為feigin配置HttpURLConnection,方法feignClient():只有以上兩個Client沒有生產對象時,才在這個方法中使用Client.Default生成LoadBalancerFeignClient
<code>@Configurationclass DefaultFeignLoadBalancedConfiguration { @Bean @ConditionalOnMissingBean public Client feignClient(CachingSpringLoadBalancerFactory cachingFactory, SpringClientFactory clientFactory) { return new LoadBalancerFeignClient(new Client.Default(null, null), cachingFactory, clientFactory); }}/<code>
查看Client.Default的源碼,Default 使用HttpURLConnection 建立連接且每次請求都建立一個新的連接
<code> 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(); } ….}/<code>
綜上所述,在默認情況下,spring cloud 沒有引入httpclient和okhttp的jar包,所有默認使用HttpURLConnection
1. 替換OKHttp
由此可見feigin優化需要引入線程池有2種可選的線程池:HttpClient和OKHttp比較推薦OKHttp,請求封裝的非常簡單易用,性能也很ok。
1.1. 添加依賴
<code><dependency> <groupid>com.squareup.okhttp3/<groupid> <artifactid>okhttp/<artifactid>/<dependency>/<code>
1.2. 修改配置文件
<code>feign: okhttp: enabled: true httpclient: enabled: false max-connections: 1000 max-connections-per-route: 100/<code>
max-connections:最大連接數max-connections-per-route:每個url的連接數
2. 開啟Feign請求響應壓縮
開啟壓縮可以有效節約網絡資源,但是會增加CPU壓力,建議把最小壓縮的文檔大小適度調大一點
<code>## 開啟Feign請求響應壓縮feign.compression.request.enabled=truefeign.compression.response.enabled=true## 配置壓縮文檔類型及最小壓縮的文檔大小feign.compression.request.mime-types=text/xml,application/xml,application/jsonfeign.compression.request.min-request-size=2048/<code>
三、Ribbon參數調優
主要調整請求的超時時間,是否重試
假如業務沒有做冪等性的話建議把重試關掉ribbon.MaxAutoRetriesNextServer=0
<code>## 從註冊中心刷新servelist的時間 默認30秒,單位msribbon.ServerListRefreshInterval=15000## 請求連接的超時時間 默認1秒,單位msribbon.ConnectTimeout=30000## 請求處理的超時時間 默認1秒,單位msribbon.ReadTimeout=30000## 對所有操作請求都進行重試,不配置這個MaxAutoRetries不起作用 默認false#ribbon.OkToRetryOnAllOperations=true## 對當前實例的重試次數 默認0#ribbon.MaxAutoRetries=1## 切換實例的重試次數 默認1ribbon.MaxAutoRetriesNextServer=0/<code>
如果MaxAutoRetries=1和MaxAutoRetriesNextServer=1請求在1s內響應,超過1秒先同一個服務器上重試1次,如果還是超時或失敗,向其他服務上請求重試1次。那麼整個ribbon請求過程的超時時間為:ribbonTimeout = (ribbonReadTimeout + ribbonConnectTimeout) * (maxAutoRetries + 1) * (maxAutoRetriesNextServer + 1)
ribbon fegin第一次請求慢問題
開啟ribbon飢餓模式
<code>ribbon: eager-load: enabled: true /<code>
feigin 日誌輸出
<code>package com.open.capacity.server.config;import org.springframework.context.annotation.Bean;import feign.Logger.Level;public class GolbalFeignConfig {@Beanpublic Level levl(){return Level.FULL;}}/<code>
<code>@EnableLogging@EnableDiscoveryClient@SpringBootApplication@EnableFeignClients(defaultConfiguration=GolbalFeignConfig.class)public class AuthServerApp {public static void main(String[] args) {//固定端口啟動//SpringApplication.run(OpenAuthServerApp.class, args);//隨機端口啟動SpringApplication app = new SpringApplication(AuthServerApp.class); app.addListeners(new PortApplicationEnvironmentPreparedEventListener()); app.run(args);}}/<code>
<code>##feign參數優化feign: client: config: default: loggerLevel: full ## 配合logging.level=trace debug用於開發調式日誌logging: level: com.open.capacity: TRACE org.hibernate: INFO org.hibernate.type.descriptor.sql.BasicBinder: TRACE org.hibernate.type.descriptor.sql.BasicExtractor: TRACE/<code>
閱讀更多 weal1992 的文章