最新要闻

广告

手机

iphone11大小尺寸是多少?苹果iPhone11和iPhone13的区别是什么?

iphone11大小尺寸是多少?苹果iPhone11和iPhone13的区别是什么?

警方通报辅警执法直播中被撞飞:犯罪嫌疑人已投案

警方通报辅警执法直播中被撞飞:犯罪嫌疑人已投案

家电

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

来源:博客园

来源:https://www.cnblogs.com/mufeng3421/p/11442412.html


【资料图】

提示:如果只看如何解决问题,请看文章的末尾如何解决这个问题

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一样

推荐一个开源免费的 Spring Boot 实战项目:

https://github.com/javastacks/spring-boot-best-practice

2.2 配置feign

在入口类上添加 @EnableFeignClients注解

@SpringBootApplication@EnableFeignClientspublic class Application {    public static void main(String[] args) {        SpringApplication.run(Application.class, args);    }}

使用feing自己的Contract,方便使用feign自己的注解来声明http接口。这里使用了一个配置类

@Configurationpublic class FeignConfig {    @Bean    public Contract feignContract() {        return new feign.Contract.Default();    }}

2.3 声明接口

只需要声明一个带有@FeignClient注解的接口,就声明好了一个Feign的http请求接口

@FeignClient(name = "accessPlatform", url = "${url.access-platform}")public interface AccessPlatformFeignClient {    @RequestLine("GET /access-platform/resource")    List queryResourceList(@QueryMap Map 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)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.ymlfeign:    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})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的配置    }  // .....其他的配置}

就是这个自动配置类搞的鬼、当我看到 @ConditionalOnMissingBean({okhttp3.OkHttpClient.class})这个注解时,我就明白了。翻成白话就是,只有当容器中没有OkHttpClient的实例时。他才会运行。如果在 FeignAutoConfiguration之前注入了我们自己定义的OkHttpClient实例,那不好意思,我不干了?无不注入。

5.解决问题

既然自动配置不干,那我们自己动手干。拷贝 FeignAutoConfiguration配置类中的配置过程,粘贴在FeignConfig配置类中手动注入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()) // 自定义请求日志拦截器                .build();    }}

6.小结

如果你没有耐心看完所有的过程的话。就记住一句话:用自己手动注入Feign的Client实现,来代替 Feign的自动配置所做的过程就好了。具体的配置请看第5点。

近期热文推荐:

1.1,000+ 道 Java面试题及答案整理(2022最新版)

2.劲爆!Java 协程要来了。。。

3.Spring Boot 2.x 教程,太全了!

4.别再写满屏的爆爆爆炸类了,试试装饰器模式,这才是优雅的方式!!

5.《Java开发手册(嵩山版)》最新发布,速速下载!

觉得不错,别忘了随手点赞+转发哦!

关键词: