SpringCloud Open feign 使用okhttp 優(yōu)化詳解
我就廢話不多說了,大家還是直接看代碼吧~
<!--web 模塊 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <exclusions> <!--排除tomcat依賴 --> <exclusion> <artifactId>spring-boot-starter-tomcat</artifactId> <groupId>org.springframework.boot</groupId> </exclusion> </exclusions> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency> <!--undertow容器 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-undertow</artifactId> </dependency> <!-- feign-okhttp --> <dependency> <groupId>io.github.openfeign</groupId> <artifactId>feign-okhttp</artifactId> </dependency>
配置pom,容器使用undertow,引入feign-okhttp
feign: # Okhttp參數(shù)配置 httpclient: enabled: false okhttp: enabled: true max-connections: 200 # 默認值 max-connections-per-route: 50 # 默認值
application.yml文件配置okhttp參數(shù)
import feign.Feign; import okhttp3.ConnectionPool; import org.springframework.boot.autoconfigure.AutoConfigureBefore; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.cloud.openfeign.FeignAutoConfiguration; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import java.util.concurrent.TimeUnit; @Configuration @ConditionalOnClass(Feign.class) @AutoConfigureBefore(FeignAutoConfiguration.class) public class FeignOkHttpConfig { @Bean public okhttp3.OkHttpClient okHttpClient(){ return new okhttp3.OkHttpClient.Builder() //設置連接超時 .connectTimeout(60, TimeUnit.SECONDS) //設置讀超時 .readTimeout(60, TimeUnit.SECONDS) //設置寫超時 .writeTimeout(120,TimeUnit.SECONDS) //是否自動重連 .retryOnConnectionFailure(true) .connectionPool(new ConnectionPool()) .addInterceptor(new OkHttpLogInterceptor()) //構建OkHttpClient對象 .build(); } }
創(chuàng)建FeignOkHttpConfig類文件
import lombok.extern.log4j.Log4j2; import okhttp3.Interceptor; import okhttp3.Request; import okhttp3.Response; import okhttp3.ResponseBody; import java.io.IOException; @Log4j2 public class OkHttpLogInterceptor implements Interceptor { @Override public Response intercept(Interceptor.Chain chain) throws IOException { //這個chain里面包含了request和response,所以你要什么都可以從這里拿 Request request = chain.request(); long t1 = System.nanoTime();//請求發(fā)起的時間 log.info(String.format("發(fā)送請求 %s on %s%n%s", request.url(), chain.connection(), request.headers())); Response response = chain.proceed(request); long t2 = System.nanoTime();//收到響應的時間 //這里不能直接使用response.body().string()的方式輸出日志 //因為response.body().string()之后,response中的流會被關閉,程序會報錯,我們需要創(chuàng)建出一 //個新的response給應用層處理 ResponseBody responseBody = response.peekBody(1024 * 1024); log.info(String.format("接收響應: [%s] %n返回json:【%s】 %.1fms%n%s", response.request().url(), responseBody.string(), (t2 - t1) / 1e6d, response.headers())); return response; } }
創(chuàng)建OkHttpLogInterceptor日志攔截文件
注意FeignOkHttpConfig中添加
@Bean public Contract feignContract() { return new feign.Contract.Default(); }
feigin請求的@PostMapping @GetMapping等會不受支持
圖一,使用默認http
圖一,F(xiàn)eign通過jdk中的HttpURLConnection
圖二,F(xiàn)eign使用okhttp請求
補充:Feign、httpclient、OkHttp3 結合使用
1 Feign 客戶端實現(xiàn) 類型
前面介紹到了常用的Feign客戶端實現(xiàn)類,大致如下:
(1) Client.Default類:默認的 feign.Client 客戶端實現(xiàn)類,內(nèi)部使用HttpURLConnnection 完成HTTP URL請求處理;
(2) ApacheHttpClient 類:內(nèi)部使用 Apache httpclient 開源組件完成HTTP URL請求處理的feign.Client 客戶端實現(xiàn)類;
(3) OkHttpClient類:內(nèi)部使用 OkHttp3 開源組件完成HTTP URL請求處理的feign.Client 客戶端實現(xiàn)類。
(4) LoadBalancerFeignClient 類:這是一個特殊的 feign.Client 客戶端實現(xiàn)類。內(nèi)部先使用 Ribbon 負載均衡算法計算server服務器,然后使用包裝的 delegate 客戶端實例,去完成 HTTP URL請求處理。
Feign 在啟動的時候,有兩個與feign.Client 客戶端實例相關的自動配置類,根據(jù)多種條件組合,去創(chuàng)建不同類型的 客戶端Spring IOC容器實例。
1.1.1 配置 LoadBalancerFeignClient 負載均衡容器實例
Feign有兩個與Client相關的自動配置類:
(1)org.springframework.cloud.openfeign.ribbon.FeignRibbonClientAutoConfiguration
(2)org.springframework.cloud.openfeign.FeignAutoConfiguration
第一個自動配置類,能夠配置具有負載均衡能力的FeignClient容器實例;第二自動配置類,只能配置最原始的FeignClient容器實例。
具備負載均衡能力的 FeignClient 容器實例,所對應的類型為 LoadBalancerFeignClient 類型。前面講到,在SpringCloud中,為了達到高可用,一個微服務至少應該部署兩個以上節(jié)點,從這個角度來說,LoadBalancerFeignClient 容器實例,已經(jīng)成為事實上的標配。
事實上,第一個自動配置類 FeignRibbonClientAutoConfiguration,在容器的裝配次序上,是優(yōu)先于第二個自動配置類 FeignAutoConfiguration 的。具體可以參見其源碼,節(jié)選如下:
import com.netflix.loadbalancer.ILoadBalancer; //…. @ConditionalOnClass({ILoadBalancer.class, Feign.class}) @Configuration @AutoConfigureBefore({FeignAutoConfiguration.class}) // 本配置類具備優(yōu)先權 @EnableConfigurationProperties({FeignHttpClientProperties.class}) @Import({ HttpClientFeignLoadBalancedConfiguration.class, //配置:包裝ApacheHttpClient實例的負載均衡客戶端 OkHttpFeignLoadBalancedConfiguration.class, //配置:包裝OkHttpClient 實例的負載均衡客戶端 DefaultFeignLoadBalancedConfiguration.class //配置:包裝Client.Default 實例的負載均衡客戶端 }) public class FeignRibbonClientAutoConfiguration { //空的構造器 public FeignRibbonClientAutoConfiguration() { } //…. }
從源碼中可以看到,F(xiàn)eignRibbonClientAutoConfiguration 的自動配置有兩個前提條件:
(1)當前的類路徑中,存在 ILoadBalancer.class 接口
(2)當前的類路徑中,存在 Feign.class 接口
在這里,重點說一下 ILoadBalancer.class 接口,該接口處于 ribbon 的jar包中。如果需要在類路徑中導入該jar包,則需要在Maven的pom.xml文件中,增加 ribbon 的相關依賴,具體如下:
<!-- ribbon--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-ribbon</artifactId> </dependency>
為了加深大家對客戶端負載均衡的理解,這里將 ILoadBalancer.class 接口的兩個重要的抽象方法列出來,具體如下:
package com.netflix.loadbalancer; import java.util.List; public interface ILoadBalancer { // 通過負載均衡算法計算server服務器 Server chooseServer(Object var1); // 取得全部的服務器 List<Server> getAllServers(); //… }
FeignRibbonClientAutoConfiguration 自動配置類,并沒有直接配置LoadBalancerFeignClient 容器實例,而是使用@Import注解,通過導入其他配置類的方式,完成 LoadBalancerFeignClient 客戶端容器實例的配置。
分別導入了以下三個自動配置類:
(1) HttpClientFeignLoadBalancedConfiguration.class
該配置類,負責配置一個包裝 ApacheHttpClient 實例的 LoadBalancerFeignClient負載均衡客戶端。
(2) OkHttpFeignLoadBalancedConfiguration.class
該配置類,負責配置一個包裝 OkHttpClient 實例的 LoadBalancerFeignClient負載均衡客戶端。
(3) DefaultFeignLoadBalancedConfiguration.class
該配置類,負責配置一個包裝 Client.Default 實例的 LoadBalancerFeignClient負載均衡客戶端。
1.1.2 包裝 ApacheHttpClient 實例的負載均衡容器實例
首先來看如何配置一個包裝 ApacheHttpClient 實例的負載均衡容器實例。這個IOC實例的配置,由 HttpClientFeignLoadBalancedConfiguration 自動配置類完成的,其源碼節(jié)選如下:
@Configuration @ConditionalOnClass({ApacheHttpClient.class}) @ConditionalOnProperty( value = {"feign.httpclient.enabled"}, matchIfMissing = true ) class HttpClientFeignLoadBalancedConfiguration { //空的構造器 HttpClientFeignLoadBalancedConfiguration() { } @Bean @ConditionalOnMissingBean({Client.class}) public Client feignClient( CachingSpringLoadBalancerFactory cachingFactory, SpringClientFactory clientFactory, HttpClient httpClient) { ApacheHttpClient delegate = new ApacheHttpClient(httpClient); return new LoadBalancerFeignClient(delegate, cachingFactory, clientFactory); // 進行包裝 } //…省略不相干的代碼 }
首先,來看源碼中的 feignClient(…)方法,分為兩步:
(1)創(chuàng)建一個 ApacheHttpClient 類型的 feign.Client客戶端實例,該實例的內(nèi)部使用 Apache httpclient 開源組件完成HTTP URL請求處理;
(2)創(chuàng)建一個 LoadBalancerFeignClient 負載均衡客戶端實例,將 ApacheHttpClient 實例包裝起來,然后返回LoadBalancerFeignClient 客戶端實例,作為 feign.Client 類型的Spring IOC 容器實例。
然后,再看類 HttpClientFeignLoadBalancedConfiguration 上的兩個重要的注解:
(1)@ConditionalOnClass(ApacheHttpClient.class)
(2)@ConditionalOnProperty(value = “feign.httpclient.enabled”, matchIfMissing = true)
這兩個條件的含義為:
(1)必須滿足 ApacheHttpClient.class 在當前類路徑中存在;
(2)必須滿足工程配置文件中 feign.httpclient.enabled 配置項的值為 true ;
如果以上兩個條件同時滿足,則 HttpClientFeignLoadBalancedConfiguration 自動配置工作就會啟動。
如何驗證呢?
首先在工程配置文件中,將配置項 feign.httpclient.enabled 的值,設置為 false 。
然后,在 HttpClientFeignLoadBalancedConfiguration 的 feignClient(…)方法內(nèi)的某行打上斷點,重新啟動項目,注意觀察會發(fā)現(xiàn),整個啟動過程中,斷點沒有被命中。
接下來,將配置項 feign.httpclient.enabled 的值設置為 true,再一次啟動項目,斷點被命中。由此,可以驗證 HttpClientFeignLoadBalancedConfiguration 自動配置類被啟動。
為了滿足 @ConditionalOnClass(ApacheHttpClient.class) 的條件要求,由于ApacheHttpClient類的位置處于feign-httpclient相關的jar包中,所以,需要在pom文件加上 feign-httpclient 以及httpclient 組件相關的 Maven 依賴,具體如下:
<dependency> <groupId>io.github.openfeign</groupId> <artifactId>feign-httpclient</artifactId> <version>9.5.1</version> <!--<version>${feign-httpclient.version}</version>--> </dependency> <dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpclient</artifactId> <version>${httpclient.version}</version> </dependency>
對于 feign.httpclient.enabled 配置項設置,根據(jù) @ConditionalOnProperty 注解的屬性matchIfMissing=true 可知,這個可以不用配置,在默認的情況下就為 true。換句話說,如果不做特別的配置,feign.httpclient.enabled 配置項的值,默認為 true。
1.1.3 包裝 OkHttpClient 實例的負載均衡容器實例
接下來,來看如何配置一個包裝 OkHttpClient 實例的負載均衡容器實例。這個IOC實例的配置,由 OkHttpFeignLoadBalancedConfiguration 自動配置類完成的,其源碼節(jié)選如下:
@Configuration @ConditionalOnClass({OkHttpClient.class}) @ConditionalOnProperty("feign.okhttp.enabled") class OkHttpFeignLoadBalancedConfiguration { //空的構造器 OkHttpFeignLoadBalancedConfiguration () { } @Bean @ConditionalOnMissingBean({Client.class}) public Client feignClient( CachingSpringLoadBalancerFactory cachingFactory, SpringClientFactory clientFactory, HttpClient httpClient) { OkHttpClient delegate = new OkHttpClient (httpClient); return new LoadBalancerFeignClient(delegate, cachingFactory, clientFactory); // 進行包裝 } //…省略不相干的代碼 }
首先,來看源碼中的 feignClient(…)方法,分為兩步:
(1)創(chuàng)建一個 OkHttpClient 類型的 feign.Client客戶端實例,該實例的內(nèi)部使用 OkHttp3 開源組件完成HTTP URL請求處理;
(2)創(chuàng)建一個 LoadBalancerFeignClient 負載均衡客戶端實例,將 OkHttpClient實例包裝起來,然后返回LoadBalancerFeignClient 客戶端實例,作為 feign.Client 類型的Spring IOC 容器實例。
然后,再看類 OkHttpFeignLoadBalancedConfiguration 上的兩個重要的注解:
(1)@ConditionalOnClass(OkHttpClient.class)
(2)@ConditionalOnProperty(“feign.okhttp.enabled”)
這兩個條件的含義為:
(1)必須滿足 OkHttpClient.class 在當前類路徑中存在;
(2)必須滿足工程配置文件中 feign.okhttp.enabled 配置項的值為 true 。
如果以上兩個條件同時滿足,則 OkHttpFeignLoadBalancedConfiguration 自動配置工作就會啟動。
為了滿足 @ConditionalOnClass(OkHttpClient.class) 的條件要求,由于OkHttpClient.class 類的位置處于 feign-okhttp 相關的jar包中,所以,需要在pom文件加上 feign-okhttp 以及 okhttp3 相關的 Maven 依賴。具體如下:
<!-- OkHttp --> <dependency> <groupId>com.squareup.okhttp3</groupId> <artifactId>okhttp</artifactId> </dependency> <!-- feign-okhttp --> <dependency> <groupId>io.github.openfeign</groupId> <artifactId>feign-okhttp</artifactId> </dependency>
對于 feign.okhttp.enabled 配置項設置,在默認的情況下就為 false。也就是說,如果需要使用feign-okhttp,則一定需要做特別的配置,在工程配置文件中,加上 feign.okhttp.enabled 配置項的值,并且值必須為 true。
如果需要使用 feign-okhttp,工程配置文件的配置項大致如下:
feign.httpclient.enabled=false feign.okhttp.enabled=true
1.1.4 包裝 Client.Default 客戶端實例的負載均衡容器實例
最后,來看如何配置一個包裝默認Client.Default 客戶端實例的負載均衡容器實例。這個IOC實例的配置,由 DefaultFeignLoadBalancedConfiguration 自動配置類所完成的。該配置類,也就是 FeignRibbonClientAutoConfiguration 配置類通過 @import 注解所導入的第3個配置類。
DefaultFeignLoadBalancedConfiguration 的源碼節(jié)選如下:
package org.springframework.cloud.openfeign.ribbon; //…省略import @Configuration class DefaultFeignLoadBalancedConfiguration { DefaultFeignLoadBalancedConfiguration() { } @Bean @ConditionalOnMissingBean public Client feignClient(CachingSpringLoadBalancerFactory cachingFactory, SpringClientFactory clientFactory) { return new LoadBalancerFeignClient( new Default((SSLSocketFactory)null, (HostnameVerifier)null), cachingFactory, clientFactory); } }
通過源碼可以看出,如果前面的兩個配置類的條件沒有滿足,feign.Client 的 IOC 容器實例沒有裝配,則:
(1) 創(chuàng)建一個 Client.Default 默認客戶端實例,該實例的內(nèi)部,使用HttpURLConnnection 完成URL請求處理;
(2) 創(chuàng)建一個 LoadBalancerFeignClient 負載均衡客戶端實例,將 Client.Default 實例包裝起來,然后返回LoadBalancerFeignClient 客戶端實例,作為 feign.Client 類型的Spring IOC 容器實例。
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。如有錯誤或未考慮完全的地方,望不吝賜教。
相關文章
SpringBoot創(chuàng)建Docker鏡像的方法步驟
這篇文章主要介紹了SpringBoot創(chuàng)建Docker鏡像的方法步驟,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2020-11-11Java用?Gradle配置compile及implementation和api的區(qū)別
這篇文章主要介紹了Java用Gradle配置compile及implementation和api的區(qū)別,文章圍繞主題的相關資料展開詳細的內(nèi)容介紹,具有一定的參考價值,需要的小伙伴可以參考一下2022-06-06詳解Spring中@Component和@Configuration的區(qū)別
一直有同學搞不清Spring中@Component和@Configuration這兩個注解有什么區(qū)別,所以這篇文章小編就給大家簡單介紹一下@Component和@Configuration的區(qū)別,需要的朋友可以參考下2023-07-07SpringBoot中的Profile多環(huán)境配置方法
這篇文章主要介紹了SpringBoot中的Profile多環(huán)境配置,SpringBoot提供了兩種多環(huán)境配置的方式,分別是使用profile文件進行多環(huán)境配置以及使用@Profile注解進行多環(huán)境配置,需要的朋友可以參考下2023-01-01