真实案例:Feign 切换 okhttp 无法生效,被老大骂的有点慌! ...

铁佛  论坛元老 | 2023-6-14 10:39:25 | 显示全部楼层 | 阅读模式
打印 上一主题 下一主题

主题 1031|帖子 1031|积分 3093

来源:https://www.cnblogs.com/mufeng3421/p/11442412.html
提示:如果只看如何解决问题,请看文章的末尾如何解决这个问题
1. 场景描述

最近项目中使用了feign当做http请求工具来使用、相对于httpclient、resttemplate来说,fegin用起来方便很多。然后项目有httptrace的需求,需要输出请求日志。
所以就开启了feign自己的日志,发现它自带的日志是debug级别才能打印。而且是逐行打印的,看日志非常的不方便。所以需要输出json格式的日志最好。
2.解决步骤

2.1 引入feign依赖
  1. <dependency>
  2.   <groupId>org.springframework.cloud</groupId>
  3.   <artifactId>spring-cloud-starter-openfeign</artifactId>
  4. </dependency>
  5. <dependencyManagement>
  6.   <dependencies>
  7.     <dependency>
  8.       <groupId>org.springframework.cloud</groupId>
  9.       <artifactId>spring-cloud-dependencies</artifactId>
  10.       <version>${自行选择适合项目的版本}</version>
  11.       <type>pom</type>
  12.       <scope>import</scope>
  13.     </dependency>
  14.   </dependencies>
  15. </dependencyManagement>
复制代码
这里使用了spring-cloud-openfeing来避免自己手工实现feign的注入,用法上和feign一样
推荐一个开源免费的 Spring Boot 实战项目:
https://github.com/javastacks/spring-boot-best-practice
2.2 配置feign

在入口类上添加 @EnableFeignClients 注解
  1. @SpringBootApplication
  2. @EnableFeignClients
  3. public class Application {
  4.     public static void main(String[] args) {
  5.         SpringApplication.run(Application.class, args);
  6.     }
  7. }
复制代码
使用feing自己的Contract,方便使用feign自己的注解来声明http接口。这里使用了一个配置类
  1. @Configuration
  2. public class FeignConfig {
  3.     @Bean
  4.     public Contract feignContract() {
  5.         return new feign.Contract.Default();
  6.     }
  7. }
复制代码
2.3 声明接口

只需要声明一个带有@FeignClient注解的接口,就声明好了一个Feign的http请求接口
  1. @FeignClient(name = "accessPlatform", url = "${url.access-platform}")
  2. public interface AccessPlatformFeignClient {
  3.     @RequestLine("GET /access-platform/resource")
  4.     List<AccessResource> queryResourceList(@QueryMap Map<String, Object> query);
  5. }
复制代码
3.切换Feign的客户端为OkHttp

由于feign自带的http客户端实现是HttpURLConnection,没有连接池功能,可配置能力也比较差,因此我们使用okhttp作为底层的http客户端的具体实现。
3.1 引入okhttp的依赖
  1. <dependency>
  2.     <groupId>io.github.openfeign</groupId>
  3.     <artifactId>feign-okhttp</artifactId>
  4.     <version>${feign-okhttp.version}</version>
  5. </dependency>
复制代码
3.2 修改之前的FeignConfig配置类

问题就出在这里、不过先不急,我们继续
  1. @Configuration
  2. @ConditionalOnClass(Feign.class)
  3. @AutoConfigureBefore(FeignAutoConfiguration.class)
  4. public class FeignConfig {
  5.     // 注入feignContract
  6.     @Bean
  7.     public Contract feignContract() {
  8.         return new feign.Contract.Default();
  9.     }
  10.     // 注入自定义的okHttpClient
  11.     @Bean
  12.     public okhttp3.OkHttpClient okHttpClient(){
  13.         return new okhttp3.OkHttpClient.Builder()
  14.             .readTimeout(60, TimeUnit.SECONDS)
  15.             .connectTimeout(60, TimeUnit.SECONDS)
  16.             .writeTimeout(120, TimeUnit.SECONDS)
  17.             .connectionPool(new ConnectionPool())
  18.             .build();
  19.     }
  20. }
复制代码
开启okhttp作为feign的客户端
  1. # application.yml
  2. feign:
  3.     okhttp:
  4.         enabled: true
复制代码
一切完美、网上的许多博客也都是这么写的。然后它们告诉你已经配置完了?呵呵,你们到底自己试过没有??
3.3 试着请求一下

当然这里出问题了,发出的请求并非来自okhttp,还是默认的JDK的HttpURLConnection,问题出在哪里呢?接着看
4.找出问题所在

我怀疑是feing在注入配置的时候,根本就没有运行关于okhttp的配置
4.1 查看服务启动时feign配置过程

1.将服务根日志级别调整为debug级别
  1. logging:
  2.     level:
  3.             root: debug
复制代码
2.启动服务、查看控制台输出

看到没,okhttp的配置不符合配置运行条件。
3.查询 FeignAutoConfiguration 这个配置类的细节
  1. @Configuration
  2. @ConditionalOnClass({Feign.class})
  3. @EnableConfigurationProperties({FeignClientProperties.class, FeignHttpClientProperties.class})
  4. public class FeignAutoConfiguration {
  5.                 // .....其他的配置
  6.           @Configuration
  7.     @ConditionalOnClass({OkHttpClient.class})
  8.     @ConditionalOnMissingClass({"com.netflix.loadbalancer.ILoadBalancer"})
  9.     @ConditionalOnMissingBean({okhttp3.OkHttpClient.class})
  10.     @ConditionalOnProperty({"feign.okhttp.enabled"})
  11.     protected static class OkHttpFeignConfiguration {
  12.        // ...okhttp的配置
  13.     }
  14.           // .....其他的配置
  15. }
复制代码
就是这个自动配置类搞的鬼、当我看到 @ConditionalOnMissingBean({okhttp3.OkHttpClient.class}) 这个注解时,我就明白了。
翻成白话就是,只有当容器中没有OkHttpClient的实例时。他才会运行。如果在 FeignAutoConfiguration之前注入了我们自己定义的OkHttpClient实例,那不好意思,我不干了?无不注入。
5.解决问题

既然自动配置不干,那我们自己动手干。拷贝 FeignAutoConfiguration 配置类中的配置过程,粘贴在FeignConfig配置类中手动注入feign的client。Ok!完美解决。
  1. @Configuration
  2. @ConditionalOnClass(Feign.class)
  3. @AutoConfigureAfter(FeignAutoConfiguration.class)
  4. public class FeignConfig {
  5. //    private okhttp3.OkHttpClient okHttpClient;
  6.     @Bean
  7.     public Contract feignContract() {
  8.         return new feign.Contract.Default();
  9.     }
  10.     @Bean
  11.     @ConditionalOnMissingBean({Client.class})
  12.     public Client feignClient(okhttp3.OkHttpClient client) {
  13.         return new feign.okhttp.OkHttpClient(client);
  14.     }
  15.     @Bean
  16.     @ConditionalOnMissingBean({ConnectionPool.class})
  17.     public ConnectionPool httpClientConnectionPool(FeignHttpClientProperties httpClientProperties, OkHttpClientConnectionPoolFactory connectionPoolFactory) {
  18.         Integer maxTotalConnections = httpClientProperties.getMaxConnections();
  19.         Long timeToLive = httpClientProperties.getTimeToLive();
  20.         TimeUnit ttlUnit = httpClientProperties.getTimeToLiveUnit();
  21.         return connectionPoolFactory.create(maxTotalConnections, timeToLive, ttlUnit);
  22.     }
  23.     @Bean
  24.     public OkHttpClient client(OkHttpClientFactory httpClientFactory, ConnectionPool connectionPool, FeignHttpClientProperties httpClientProperties) {
  25.         Boolean followRedirects = httpClientProperties.isFollowRedirects();
  26.         Integer connectTimeout = httpClientProperties.getConnectionTimeout();
  27.         Boolean disableSslValidation = httpClientProperties.isDisableSslValidation();
  28.         return httpClientFactory.createBuilder(disableSslValidation)
  29.                 .connectTimeout((long)connectTimeout, TimeUnit.MILLISECONDS)
  30.                 .followRedirects(followRedirects)
  31.                 .connectionPool(connectionPool)
  32.                 .addInterceptor(new OkHttpLogInterceptor()) // 自定义请求日志拦截器
  33.                 .build();
  34.     }
  35. }
复制代码
6.小结

如果你没有耐心看完所有的过程的话。就记住一句话:用自己手动注入Feign的Client实现,来代替 Feign的自动配置所做的过程就好了。具体的配置请看第5点。
近期热文推荐:
1.1,000+ 道 Java面试题及答案整理(2022最新版)
2.劲爆!Java 协程要来了。。。
3.Spring Boot 2.x 教程,太全了!
4.别再写满屏的爆爆爆炸类了,试试装饰器模式,这才是优雅的方式!!
5.《Java开发手册(嵩山版)》最新发布,速速下载!
觉得不错,别忘了随手点赞+转发哦!

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

铁佛

论坛元老
这个人很懒什么都没写!
快速回复 返回顶部 返回列表