解決Feign切換client到okhttp無(wú)法生效的坑(出現(xiàn)原因說(shuō)明)
提示:如果只看如何解決問(wèn)題,請(qǐng)看文章的末尾如何解決這個(gè)問(wèn)題
1. 場(chǎng)景描述
最近項(xiàng)目中使用了feign當(dāng)做http請(qǐng)求工具來(lái)使用、相對(duì)于httpclient、resttemplate來(lái)說(shuō),fegin用起來(lái)方便很多。
然后項(xiàng)目有httptrace的需求,需要輸出請(qǐng)求日志。
所以就開啟了feign自己的日志,發(fā)現(xiàn)它自帶的日志是debug級(jí)別才能打印。而且是逐行打印的,看日志非常的不方便。所以需要輸出json格式的日志最好。
2.解決步驟
2.1 引入feign依賴
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${自行選擇適合項(xiàng)目的版本}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
這里使用了spring-cloud-openfeing來(lái)避免自己手工實(shí)現(xiàn)feign的注入,用法上和feign一樣
2.2 配置feign
在入口類上添加 @EnableFeignClients 注解
@SpringBootApplication
@EnableFeignClients
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
使用feing自己的Contract,方便使用feign自己的注解來(lái)聲明http接口。這里使用了一個(gè)配置類
@Configuration
public class FeignConfig {
@Bean
public Contract feignContract() {
return new feign.Contract.Default();
}
}
2.3 聲明接口
只需要聲明一個(gè)帶有@FeignClient注解的接口,就聲明好了一個(gè)Feign的http請(qǐng)求接口
@FeignClient(name = "accessPlatform", url = "${url.access-platform}")
public interface AccessPlatformFeignClient {
@RequestLine("GET /access-platform/resource")
List<AccessResource> queryResourceList(@QueryMap Map<String, Object> query);
}
3.切換Feign的客戶端為OkHttp
由于feign自帶的http客戶端實(shí)現(xiàn)是HttpURLConnection,沒有連接池功能,可配置能力也比較差,因此我們使用okhttp作為底層的http客戶端的具體實(shí)現(xiàn)。
3.1 引入okhttp的依賴
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-okhttp</artifactId>
<version>${feign-okhttp.version}</version>
</dependency>
3.2 修改之前的FeignConfig配置類
問(wèn)題就出在這里、不過(guò)先不急,我們繼續(xù)
@Configuration
@ConditionalOnClass(Feign.class)
@AutoConfigureBefore(FeignAutoConfiguration.class)
public class FeignConfig {
// 注入feignContract
@Bean
public Contract feignContract() {
return new feign.Contract.Default();
}
// 注入自定義的okHttpClient
@Bean
public okhttp3.OkHttpClient okHttpClient(){
return new okhttp3.OkHttpClient.Builder()
.readTimeout(60, TimeUnit.SECONDS)
.connectTimeout(60, TimeUnit.SECONDS)
.writeTimeout(120, TimeUnit.SECONDS)
.connectionPool(new ConnectionPool())
.build();
}
}
開啟okhttp作為feign的客戶端
# application.yml feign: okhttp: enabled: true
一切完美、網(wǎng)上的許多博客也都是這么寫的。然后它們告訴你已經(jīng)配置完了?呵呵,你們到底自己試過(guò)沒有??
3.3 試著請(qǐng)求一下
當(dāng)然這里出問(wèn)題了,發(fā)出的請(qǐng)求并非來(lái)自okhttp,還是默認(rèn)的JDK的HttpURLConnection,問(wèn)題出在哪里呢?接著看
4.找出問(wèn)題所在
我懷疑是feing在注入配置的時(shí)候,根本就沒有運(yùn)行關(guān)于okhttp的配置
4.1 查看服務(wù)啟動(dòng)時(shí)feign配置過(guò)程
1.將服務(wù)根日志級(jí)別調(diào)整為debug級(jí)別
logging: level: root: debug
2.啟動(dòng)服務(wù)、查看控制臺(tái)輸出

看到?jīng)],okhttp的配置不符合配置運(yùn)行條件。
3.查詢 FeignAutoConfiguration 這個(gè)配置類的細(xì)節(jié)
@Configuration
@ConditionalOnClass({Feign.class})
@EnableConfigurationProperties({FeignClientProperties.class, FeignHttpClientProperties.class})
public class FeignAutoConfiguration {
// .....其他的配置
@Configuration
@ConditionalOnClass({OkHttpClient.class})
@ConditionalOnMissingClass({"com.netflix.loadbalancer.ILoadBalancer"})
@ConditionalOnMissingBean({okhttp3.OkHttpClient.class})
@ConditionalOnProperty({"feign.okhttp.enabled"})
protected static class OkHttpFeignConfiguration {
// ...okhttp的配置
}
// .....其他的配置
}
就是這個(gè)自動(dòng)配置類搞的鬼、當(dāng)我看到 @ConditionalOnMissingBean({okhttp3.OkHttpClient.class}) 這個(gè)注解時(shí),我就明白了。
翻成白話就是,只有當(dāng)容器中沒有OkHttpClient的實(shí)例時(shí)。他才會(huì)運(yùn)行。
如果在 FeignAutoConfiguration之前注入了我們自己定義的OkHttpClient實(shí)例,那不好意思,我不干了?無(wú)不注入。
5.解決問(wèn)題
既然自動(dòng)配置不干,那我們自己動(dòng)手干??截?FeignAutoConfiguration 配置類中的配置過(guò)程,粘貼在FeignConfig配置類中手動(dòng)注入feign的client。Ok!完美解決。
@Configuration
@ConditionalOnClass(Feign.class)
@AutoConfigureAfter(FeignAutoConfiguration.class)
public class FeignConfig {
// private okhttp3.OkHttpClient okHttpClient;
@Bean
public Contract feignContract() {
return new feign.Contract.Default();
}
@Bean
@ConditionalOnMissingBean({Client.class})
public Client feignClient(okhttp3.OkHttpClient client) {
return new feign.okhttp.OkHttpClient(client);
}
@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 OkHttpClient client(OkHttpClientFactory httpClientFactory, ConnectionPool connectionPool, FeignHttpClientProperties httpClientProperties) {
Boolean followRedirects = httpClientProperties.isFollowRedirects();
Integer connectTimeout = httpClientProperties.getConnectionTimeout();
Boolean disableSslValidation = httpClientProperties.isDisableSslValidation();
return httpClientFactory.createBuilder(disableSslValidation)
.connectTimeout((long)connectTimeout, TimeUnit.MILLISECONDS)
.followRedirects(followRedirects)
.connectionPool(connectionPool)
.addInterceptor(new OkHttpLogInterceptor()) // 自定義請(qǐng)求日志攔截器
.build();
}
}
6.小結(jié)
如果你沒有耐心看完所有的過(guò)程的話。就記住一句話:用自己手動(dòng)注入Feign的Client實(shí)現(xiàn),來(lái)代替 Feign的自動(dòng)配置所做的過(guò)程就好了。
具體的配置請(qǐng)看第5點(diǎn)
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教。
相關(guān)文章
Java靜態(tài)方法不能調(diào)用非靜態(tài)成員的原因分析
在Java中,靜態(tài)方法是屬于類的方法,而不是屬于對(duì)象的方法,它可以通過(guò)類名直接調(diào)用,無(wú)需創(chuàng)建對(duì)象實(shí)例,非靜態(tài)成員指的是類的實(shí)例變量和實(shí)例方法,它們需要通過(guò)對(duì)象實(shí)例才能訪問(wèn)和調(diào)用,本文小編將和大家一起探討Java靜態(tài)方法為什么不能調(diào)用非靜態(tài)成員2023-10-10
SpringBoot基于過(guò)濾器和內(nèi)存實(shí)現(xiàn)重復(fù)請(qǐng)求攔截功能
這篇文章主要介紹了SpringBoot基于過(guò)濾器和內(nèi)存實(shí)現(xiàn)重復(fù)請(qǐng)求攔截,這里我們使用過(guò)濾器的方式對(duì)進(jìn)入服務(wù)器的請(qǐng)求進(jìn)行過(guò)濾操作,實(shí)現(xiàn)對(duì)相同客戶端請(qǐng)求同一個(gè)接口的過(guò)濾,需要的朋友可以參考下2023-01-01
mybatis-plus IdWorker生成的Id和返回給前臺(tái)的不一致的解決
這篇文章主要介紹了mybatis-plus IdWorker生成的Id和返回給前臺(tái)的不一致的解決,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2021-03-03
mybatis if傳入字符串?dāng)?shù)字踩坑記錄及解決
這篇文章主要介紹了mybatis if傳入字符串?dāng)?shù)字踩坑記錄及解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-02-02
eclipse實(shí)現(xiàn)ElGamal數(shù)字簽名
這篇文章主要為大家詳細(xì)介紹了eclipse實(shí)現(xiàn)ElGamal數(shù)字簽名,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-06-06
Java生成N個(gè)不重復(fù)的隨機(jī)數(shù)的三種方法總結(jié)
這篇文章主要為大家詳細(xì)介紹了三種Java生成N個(gè)不重復(fù)的隨機(jī)數(shù)的方法,文中的示例代碼講解詳細(xì),具有一定的參考價(jià)值,有需要的可以了解下2023-10-10
java中break和continue區(qū)別及使用場(chǎng)合分析
本文力圖通過(guò)實(shí)例加使用場(chǎng)合詳解來(lái)引導(dǎo)菜鳥重新認(rèn)識(shí)break和continue語(yǔ)句,需要的朋友可以參考下2014-01-01
SpringMVC通過(guò)注解獲得參數(shù)的實(shí)例
下面小編就為大家?guī)?lái)一篇SpringMVC通過(guò)注解獲得參數(shù)的實(shí)例。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-08-08

