解决Feign切换client到okhttp无法生效的坑(出现原因说明)
提示:如果只看如何解决问题,请看文章的末尾如何解决这个问题
1.场景描述
最近项目中使用了feign当做http请求工具来使用、相对于httpclient、resttemplate来说,fegin用起来方便很多。
然后项目有httptrace的需求,需要输出请求日志。
所以就开启了feign自己的日志,发现它自带的日志是debug级别才能打印。而且是逐行打印的,看日志非常的不方便。所以需要输出json格式的日志最好。
2.解决步骤
2.1引入feign依赖
org.springframework.cloud spring-cloud-starter-openfeign org.springframework.cloud spring-cloud-dependencies ${自行选择适合项目的版本} pom import
这里使用了spring-cloud-openfeing来避免自己手工实现feign的注入,用法上和feign一样
2.2配置feign
在入口类上添加@EnableFeignClients注解
@SpringBootApplication @EnableFeignClients publicclassApplication{ publicstaticvoidmain(String[]args){ SpringApplication.run(Application.class,args); } }
使用feing自己的Contract,方便使用feign自己的注解来声明http接口。这里使用了一个配置类
@Configuration publicclassFeignConfig{ @Bean publicContractfeignContract(){ returnnewfeign.Contract.Default(); } }
2.3声明接口
只需要声明一个带有@FeignClient注解的接口,就声明好了一个Feign的http请求接口
@FeignClient(name="accessPlatform",url="${url.access-platform}") publicinterfaceAccessPlatformFeignClient{ @RequestLine("GET/access-platform/resource") ListqueryResourceList(@QueryMapMap query); }
3.切换Feign的客户端为OkHttp
由于feign自带的http客户端实现是HttpURLConnection,没有连接池功能,可配置能力也比较差,因此我们使用okhttp作为底层的http客户端的具体实现。
3.1引入okhttp的依赖
io.github.openfeign feign-okhttp ${feign-okhttp.version}
3.2修改之前的FeignConfig配置类
问题就出在这里、不过先不急,我们继续
@Configuration @ConditionalOnClass(Feign.class) @AutoConfigureBefore(FeignAutoConfiguration.class) publicclassFeignConfig{ //注入feignContract @Bean publicContractfeignContract(){ returnnewfeign.Contract.Default(); } //注入自定义的okHttpClient @Bean publicokhttp3.OkHttpClientokHttpClient(){ returnnewokhttp3.OkHttpClient.Builder() .readTimeout(60,TimeUnit.SECONDS) .connectTimeout(60,TimeUnit.SECONDS) .writeTimeout(120,TimeUnit.SECONDS) .connectionPool(newConnectionPool()) .build(); } }
开启okhttp作为feign的客户端
#application.yml feign: okhttp: enabled:true
一切完美、网上的许多博客也都是这么写的。然后它们告诉你已经配置完了?呵呵,你们到底自己试过没有??
3.3试着请求一下
当然这里出问题了,发出的请求并非来自okhttp,还是默认的JDK的HttpURLConnection,问题出在哪里呢?接着看
4.找出问题所在
我怀疑是feing在注入配置的时候,根本就没有运行关于okhttp的配置
4.1查看服务启动时feign配置过程
1.将服务根日志级别调整为debug级别
logging: level: root:debug
2.启动服务、查看控制台输出
看到没,okhttp的配置不符合配置运行条件。
3.查询FeignAutoConfiguration这个配置类的细节
@Configuration @ConditionalOnClass({Feign.class}) @EnableConfigurationProperties({FeignClientProperties.class,FeignHttpClientProperties.class}) publicclassFeignAutoConfiguration{ //.....其他的配置 @Configuration @ConditionalOnClass({OkHttpClient.class}) @ConditionalOnMissingClass({"com.netflix.loadbalancer.ILoadBalancer"}) @ConditionalOnMissingBean({okhttp3.OkHttpClient.class}) @ConditionalOnProperty({"feign.okhttp.enabled"}) protectedstaticclassOkHttpFeignConfiguration{ //...okhttp的配置 } //.....其他的配置 }
就是这个自动配置类搞的鬼、当我看到@ConditionalOnMissingBean({okhttp3.OkHttpClient.class})这个注解时,我就明白了。
翻成白话就是,只有当容器中没有OkHttpClient的实例时。他才会运行。
如果在FeignAutoConfiguration之前注入了我们自己定义的OkHttpClient实例,那不好意思,我不干了?无不注入。
5.解决问题
既然自动配置不干,那我们自己动手干。拷贝FeignAutoConfiguration配置类中的配置过程,粘贴在FeignConfig配置类中手动注入feign的client。Ok!完美解决。
@Configuration @ConditionalOnClass(Feign.class) @AutoConfigureAfter(FeignAutoConfiguration.class) publicclassFeignConfig{ //privateokhttp3.OkHttpClientokHttpClient; @Bean publicContractfeignContract(){ returnnewfeign.Contract.Default(); } @Bean @ConditionalOnMissingBean({Client.class}) publicClientfeignClient(okhttp3.OkHttpClientclient){ returnnewfeign.okhttp.OkHttpClient(client); } @Bean @ConditionalOnMissingBean({ConnectionPool.class}) publicConnectionPoolhttpClientConnectionPool(FeignHttpClientPropertieshttpClientProperties,OkHttpClientConnectionPoolFactoryconnectionPoolFactory){ IntegermaxTotalConnections=httpClientProperties.getMaxConnections(); LongtimeToLive=httpClientProperties.getTimeToLive(); TimeUnitttlUnit=httpClientProperties.getTimeToLiveUnit(); returnconnectionPoolFactory.create(maxTotalConnections,timeToLive,ttlUnit); } @Bean publicOkHttpClientclient(OkHttpClientFactoryhttpClientFactory,ConnectionPoolconnectionPool,FeignHttpClientPropertieshttpClientProperties){ BooleanfollowRedirects=httpClientProperties.isFollowRedirects(); IntegerconnectTimeout=httpClientProperties.getConnectionTimeout(); BooleandisableSslValidation=httpClientProperties.isDisableSslValidation(); returnhttpClientFactory.createBuilder(disableSslValidation) .connectTimeout((long)connectTimeout,TimeUnit.MILLISECONDS) .followRedirects(followRedirects) .connectionPool(connectionPool) .addInterceptor(newOkHttpLogInterceptor())//自定义请求日志拦截器 .build(); } }
6.小结
如果你没有耐心看完所有的过程的话。就记住一句话:用自己手动注入Feign的Client实现,来代替Feign的自动配置所做的过程就好了。
具体的配置请看第5点
以上为个人经验,希望能给大家一个参考,也希望大家多多支持毛票票。如有错误或未考虑完全的地方,望不吝赐教。
声明:本文内容来源于网络,版权归原作者所有,内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎发送邮件至:czq8825#qq.com(发邮件时,请将#更换为@)进行举报,并提供相关证据,一经查实,本站将立刻删除涉嫌侵权内容。