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

1. 场景描述

最近项目中使用了feign当做http请求工具来使用、相对于httpclient、resttemplate来说,fegin用起来方便很多。然后项目有httptrace的需求,需要输出请求日志。所以就开启了feign自己的日志,发现它自带的日志是debug级别才能打印。而且是逐行打印的,看日志非常的不方便。所以需要输出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>${自行选择适合项目的版本}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>

这里使用了spring-cloud-openfeing来避免自己手工实现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自己的注解来声明http接口。这里使用了一个配置类

@Configuration
public 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<AccessResource> queryResourceList(@QueryMap Map<String, Object> query);
}

3.切换Feign的客户端为OkHttp

由于feign自带的http客户端实现是HttpURLConnection,没有连接池功能,可配置能力也比较差,因此我们使用okhttp作为底层的http客户端的具体实现。

3.1 引入okhttp的依赖

 <dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-okhttp</artifactId>
<version>${feign-okhttp.version}</version>
</dependency>

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.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})
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点

Feign切换client到okhttp无法生效天坑!(附带发生的原因)的更多相关文章

  1. springMVC+Mybatis(使用AbstractRoutingDataSource实现多数据源切换时)事务管理未生效的解决办法

    业务场景: A.B两个单位,系统部署同一套代码: A.B两系统能相互访问: 要求将数据从A系统同步到B系统,再将反馈信息回发给A: 实际开发情况: 因为系统比较小,最开始设计架构的时候没有考虑到消息互 ...

  2. Feign【替换默认的feign client】

    说明: feign默认情况下使用的是JDK原始的URLConnection发送的HTTP请求,没有使用到连接池,但是对每个地址会保持长连接,即HTTP的persistence connection.我 ...

  3. Feign Client 原理和使用

    Feign Client 原理和使用 一块石头 ​ 公众号:好奇心森林 ​关注他 创作声明:内容包含虚构创作 6 人赞同了该文章 最近一个新项目在做后端HTTP库技术选型的时候对比了Spring We ...

  4. Spring Cloud 组件 —— feign

    feign 作为一个声明式的 Http Client 开源项目.在微服务领域,相比于传统的 apache httpclient 与在 spring 中较为活跃的 RestTemplate 更面向服务化 ...

  5. SpringCloud Feign的分析

    Feign是一个声明式的Web Service客户端,它使得编写Web Serivce客户端变得更加简单.我们只需要使用Feign来创建一个接口并用注解来配置它既可完成. @FeignClient(v ...

  6. 对Feign的请求url 重写

    需求:对当前请求的 url 重新构建 debug feign 的执行可知,重写 LoadBalancerFeignClient 类中的 execute 方法即可控制当前请求的url 代码分析 当引入  ...

  7. Spring Cloud Feign 总结

    Spring Cloud中, 服务又该如何调用 ? 各个服务以HTTP接口形式暴露 , 各个服务底层以HTTP Client的方式进行互相访问. SpringCloud开发中,Feign是最方便,最为 ...

  8. Spring-Cloud之Feign声明式调用-4

    一.Feign受Retrofit.JAXRS-2.0和WebSocket影响,采用了声明式API 接口的风格,将Java Http 客户端绑定到它的内部. Feign 首要目的是将 Java Http ...

  9. SpringCloud 源码系列(6)—— 声明式服务调用 Feign

    SpringCloud 源码系列(1)-- 注册中心 Eureka(上) SpringCloud 源码系列(2)-- 注册中心 Eureka(中) SpringCloud 源码系列(3)-- 注册中心 ...

随机推荐

  1. 斗鱼刷弹幕js代码

    对于一个网络喷子(like me)来说,喷人必须高效. var script=document.createElement("script"); script.type=" ...

  2. mysql判断是否包含某个字符的方法

    mysql判断是否包含某个字符的方法用locate 是最快的,like 最慢.position一般实战例子:select * from historydatawhere locate('0',open ...

  3. java中方法的重载和覆盖分别要满足的条件

    1.重载:遵循“两同三不同” 两同:同一个类中的同名方法 三不同:形参的类型,个数,顺序不同 特别提醒: 返回值不同构不能方法重载 形参名称不同构不成方法重载 2.覆盖(重写)的要求 子类方法的名称, ...

  4. windows 下 创建项目的虚拟环境

    一. 为何使用虚拟环境 虚拟环境是Python解释器的一个私有副本,在这个环境你可以安装私有包,而且不会影响系统中安装的全局Python解释器. 在这个虚拟环境中的所有安装包,都是针对此环境的,不会被 ...

  5. Jenkins参数化构建(七)

    一.配置参数化构建过程 主要用来区分分支,使用传参的方式,将分支名称传入脚本中进行拉取代码. 1.1 最常用的是:字符参数.文本参数.  1.2 添加字符参数和文本参数,并配置变量名称  1.3 配置 ...

  6. Rust中的哈希Map

    严谨! fn main() { use std::collections::HashMap; let mut scores = HashMap::new(); scores.insert(String ...

  7. 代码审计-md5()函数

    <?php error_reporting(0); $flag = 'flag{test}'; if (isset($_GET['username']) and isset($_GET['pas ...

  8. python调用oracle存储过程

    oracle 存储过程 python调用oracle存储过程 -- 通过cx_Oracle连接 import cx_Oracle # 连接数据库 orcl_engine = 'scott/s123@x ...

  9. IDEA中的模板文件设置

    idea中的设置 Settings>Editor>File and code Templates>Includes>File Header 中粘贴下面的表达格式 /** * @ ...

  10. Django项目中出现的错误及解决办法(ValueError: Dependency on app with no migrations: customuser)

    写项目的时候遇到了类似的问题,其实就是没有生成迁移文件,执行一下数据库迁移命令就好了 ValueError: Dependency on app with no migrations: customu ...