文章目录
1.OpenFeign介绍
在使用 Spring Cloud 开发微服务应用时,各个服务提供者都是以 HTTP 接口的形式对外提供服务,因此在服务消费者调用服务提供者时,底层通过 HTTP Client 的方式访问。此时可以使用 JDK 原生的 URLConnection、Apache 的 HTTP Client、Netty 的异步 HTTP Client 或者 Spring 的 RestTemplate 去实现服务间的调用。但是最方便、最优雅的方式是通过 Feign 进行服务间的调用。Feign 是由 Netflix 开发的一个声明式的 Web Service 客户端,它的出现使开发 Web Service 客户端变得很简单;Feign 同时也是一款声明式、模板化的 HTTP 客户端
Spring Cloud OpenFeign 对 Feign 进行了二次封装,使得在 Spring Cloud 中使用 Feign 的时候,可以做到使用 HTTP 请求访问远程服务,就像调用本地方法一样的,开发者完全感知不到这是在调用远程访问,更感知不到在访问 HTTP 请求。Spring Cloud OpenFeign 增强了 Feign 的功能,使 Feign 有限支持 Spring MVC 的注解,如 @RequestMapping
等。OpenFeign 的 @FeignClient
注解可以解析 Spring MVC 的 @RequestMapping
注解下的接口
2 .快速开始
本 文 实 例 使 用 的 S p r i n g C l o u d 版 本 是 : 2020.0.4 , S p r i n g B o o t 版 本 是 : 2.5.6 本文实例使用的Spring Cloud版本是:2020.0.4,Spring Boot版本是:2.5.6 本文实例使用的SpringCloud版本是:2020.0.4,SpringBoot版本是:2.5.6
-
引入依赖
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency>
-
编写调用接口,并使用
@FeignClient
注解@FeignClient(name = "order-service-resume",path ="/order") public interface ResumeServuceFeignClient { @RequestMapping("/findResumeByUserId/{userId}") Resume findResumeByUserId(@PathVariable Long userId); @RequestMapping("/getPort") Integer getPort(); }
- name: 指定调用rest接口所对应的服务名称
- path:指定调用rest接口Controller的@RequestMapping("/order")
-
在启动类添加
@EnableFeignClients
注解@SpringBootApplication @EnableDiscoveryClient @EnableFeignClients @LoadBalancerClients(defaultConfiguration = {LoadBalancerConfiguration.class}) public class AutodeliverApplication8091 { public static void main(String[] args) { SpringApplication.run(AutodeliverApplication8091.class,args); } }
-
发起调用,像调用本地方法一样调用远程服务
@Slf4j @RestController @RequestMapping("/test") public class AutoDeliverController { @Autowired ResumeServuceFeignClient feignClient; @GetMapping("/testLoadBalanced") public Integer getPort(){ return feignClient.getPort(); } }
3 OpenFeign自定义配置
3.1 日志配置
-
定义一个配置类,指定日志级别
public class FeignConfig { @Bean public Logger.Level feignLoggerLevel() { return Logger.Level.FULL; } }
OpenFeign的日志级别有4种,分别是:
-
NONE:不记录任何日志(默认值)。适合生产环境。
-
BASIC:仅记录请求⽅法、URL、响应状态码以及执⾏时间。适合⽣产问题追踪
-
HEADERS:在BASIC级别的基础上,记录请求和响应的header。
-
FULL:记录请求和响应的header、body和元数据。适⽤于开发及测试环境定位问题
-
-
局部配置,让调用的微服务生效,在
@FeignClient
注解中指定配置类@FeignClient(name = "order-service",path ="/order",configuration = FeignConfig.class) public interface ResumeServuceFeignClient { @RequestMapping("/findResumeByUserId/{userId}") Resume findResumeByUserId(@PathVariable Long userId); @RequestMapping("/getPort") Integer getPort(); }
如果在
FeignConfig
加上@Configuration
注解,则此配置全局生效,如果想要指定的微服务生效,则不能添加@Configuration
注解。 -
在配置文件中配置
可以通过指定feignName,实现局部配置,只让某个客户端生效。比如,只让
order-service
微服务生效:feign: client: config: order-service: loggerLevel: full
如果想要全局配置,可以指定
default
,,下面的配置是全局生效。feign: client: config: default: loggerLevel: full
如果通过 Java 代码的方式配置过 Feign,然后又通过 application.yml
或者 application.properties
属性文件的方式配置 Feign,默认情况下属性文件中 Feign 的配置会覆盖 Java 代码的配置。但是可以通过使用参数 feign.client.default-to-properties=false
来改变 Feign 配置生效的优先级。
3.2 契约(Contract)配置
Spring Cloud在Feign的基础上做了扩展,使用SpringMVC的注解完成Feign的功能。原生的Feign是不支持SpringMVC注解的,如果你想在Spring Cloud中使用原生的注解方式来定义客户浩也是可以的,通过配置契约来改变这个配置,Spring Cloud 中默认的是 SpringMvcContract。
Spring Cloud早期版本就是用的原生Feign,随着netflix的停更替换成了openfeign
-
修改契约配置,支持原生Feign的注解
public class FeignConfig { @Bean public Contract feignContract() { return new feign.Contract.Default(); } }
修改契约配置后,
@FeignClient
不在支持SpringMVC注解,需要使用Feign原生的注解。
3.3 超时时间配置
我们可以在默认客户端和命名客户端上配置超时。 OpenFeign 使用两个超时参数:
connectTimeout
连接超时时间,默认2sreadTimeout
请求处理超时时间,默认5s
以下是在默认客户端配置,全局生效:
feign:
client:
config:
default:
connectTimeout: 5000 #连接超时时间,默认2s
readTimeout: 2000 # 请求处理超时时间,默认5s
loggerLevel: basic
在Java类中配置超时时间:
@Bean
public Request.Options options(){
return new Request.Options(5000,10000);
}
3.4 Feign 默认 Http Client 修改
Feign 在默认情况下使用的是 JDK 原生的 URLConnection 发送 HTTP 请求,没有使用连接池。
Feign支持以下Http请求客户端:
- OkHttpClient :通过
feign.okhttp.enabled=true
配置开启 - ApacheHttpClient: 通过
feign.httpclient.enabled=true
配置开启 - ApacheHC5 :通过
feign.httpclient.hc5.enabled=true
配置开启
使用 Apache HTTP Client 替换 Feign 默认的 Client
替换步骤非常简单,只需要在 pom.xml
与 application.yml
分别添加以下配置内容即可:
- 添加依赖:
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-httpclient</artifactId>
</dependency>
-
添加配置
feign: httpclient: enabled: true
3.5 创建多个具有相同名称或 url 的 feign 客户端
如果我们想创建多个具有相同名称或 url 的 feign 客户端,以便它们指向同一服务器但每个具有不同的自定义配置,那么我们必须使用 @FeignClient
的 contextId
属性以避免这些配置的名称冲突Bean。
@FeignClient(contextId = "fooClient", name = "stores", configuration = FooConfiguration.class)
public interface FooClient {
//..
}
@FeignClient(contextId = "barClient", name = "stores", configuration = BarConfiguration.class)
public interface BarClient {
//..
}
也可以将 FeignClient 配置为不从父上下文继承 bean。您可以通过覆盖 FeignClientConfigurer bean 中的 inheritParentConfiguration() 以返回 false 来实现此目的:
@Configuration
public class CustomConfiguration{
@Bean
public FeignClientConfigurer feignClientConfigurer() {
return new FeignClientConfigurer() {
@Override
public boolean inheritParentConfiguration() {
return false;
}
};
}
}
3.6 自定义拦截器实现认证逻辑
-
实现
RequestInterceptor
接口public class FeignAuthRequestInterceptor implements RequestInterceptor { @Override public void apply(RequestTemplate requestTemplate) { //模拟token String access_token= UUID.randomUUID().toString(); //在head中添加authorization参数 requestTemplate.header("authorization",access_token); } }
-
在配置类中加入自定义的拦截器
public class FeignConfig { /** * 添加自定义拦截器 * @return */ public FeignAuthRequestInterceptor feignAuthRequestInterceptor(){ return new FeignAuthRequestInterceptor(); } }
@FeignClient(name = "order-service-resume",path ="/order",configuration = FeignConfig.class)
public interface ResumeServuceFeignClient {
@RequestMapping("/getPort")
Integer getPort();
}
4 Feign负载均衡
spring-cloud-starter-openfeign
支持 spring-cloud-starter-loadbalancer
。但是,作为一个可选的依赖项,如果您想使用它,您需要确保将其添加到您的项目中。
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>
在最新版的Openfeign中,默认已经不再支持Ribbon
负载均衡器,需要使用官方开发的loadbalancer
。
在项目开发中需要注意:(我这里使用的版本是:Spring Cloud版本是:2020.0.4,Spring Boot版本是:2.5.6)
-
如果项目中,使用了Eureka作为注册中心,
spring-cloud-starter-netflix-eureka-client
,中包含了spring-cloud-starter-loadbalancer
的依赖,如果已经引入了eureka组件,可以不用单独引入spring-cloud-starter-loadbalancer
。 -
如果使用阿里巴巴的Nacos作为注册中心:(spring-cloud-alibaba 版本是:2.2.6.RELEASE)
在
spring-cloud-starter-alibaba-nacos-discovery
默认使用Ribbon
负载均衡,在使用OpenFeign的时候需要单独引入spring-cloud-starter-loadbalancer
,同时还需要排除掉nacos中的Ribbon,不然项目启动会报错。<dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> <exclusions> <exclusion> <artifactId>spring-cloud-starter-netflix-ribbon</artifactId> <groupId>org.springframework.cloud</groupId> </exclusion> </exclusions> </dependency>
默认的负载均衡算法为RoundRobinLoadBalancer
(轮询)
关于loadbalancer的使用方法,可以参考我的另外一篇博文:https://blog.csdn.net/warybee/article/details/121284159?spm=1001.2014.3001.5501
5 Feign 熔断器支持
Spring Cloud熔断器提供了跨不同断路器抽象的实现,支持如下熔断器:
在OpenFeign中使用熔断器,也很简单,下面以Hystrix为例。
关于Hystrix的详细使用,可以参考我的另一篇博文:https://blog.csdn.net/warybee/article/details/121284365?spm=1001.2014.3001.5501
-
添加Hystrix依赖
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-hystrix</artifactId> <version>2.2.9.RELEASE</version> </dependency>
-
在配置文件中开启
feign.circuitbreaker.enabled=true
feign: client: config: default: connectTimeout: 5000 #连接超时时间,默认2s readTimeout: 2000 # 请求处理超时时间,默认5s loggerLevel: basic circuitbreaker: #开启断路器 enabled: true
-
自定义fallback
@FeignClient(name = "stock-service",path = "stock",fallback = StockFeignClientFallback.class ) public interface StockFeignClient { @RequestMapping("getPort") public Integer getPort(); }
@Component public class StockFeignClientFallback implements StockFeignClient { @Override public Integer getPort() { return -1; } }
OpenFeign 熔断器官网文档:https://docs.spring.io/spring-cloud-openfeign/docs/current/reference/html/#spring-cloud-feign-circuitbreaker
Spring Cloud 熔断器官网文档:https://spring.io/projects/spring-cloud-circuitbreaker
6 Feign的配置属性
可以在 application.properties 文件、application.yml 文件或命令行开关中指定各种属性。本附录提供了常见 Spring Cloud OpenFeign 属性的列表以及对使用它们的底层类的引用。
名称 | 默认值 | 描述 |
---|---|---|
feign.autoconfiguration.jackson.enabled | false | 如果为 true,将为 Jackson 页面解码提供 PageJacksonModule 和 SortJacksonModule bean。 |
feign.circuitbreaker.enabled | false | 如果为 true,则 OpenFeign 客户端将使用 Spring Cloud CircuitBreaker 断路器包装。 |
feign.circuitbreaker.group.enabled | false | 如果为 true,则 OpenFeign 客户端将使用带有组的 Spring Cloud CircuitBreaker 断路器包装。 |
feign.client.config | ||
feign.client.decode-slash | true | Feign 客户端默认不编码斜杠/字符。要更改此行为,请将 decodeSlash 设置为 false。 |
feign.client.default-config | default | |
feign.client.default-to-properties | true | |
feign.client.refresh-enabled | false | 为 Feign 启用选项值刷新功能。 |
feign.compression.request.enabled | false | 使 Feign 发送的请求能够被压缩。 |
feign.compression.request.mime-types | [text/xml, application/xml, application/json] | 支持的 MIME 类型列表。 |
feign.compression.request.min-request-size | 2048 | 最小阈值内容大小。 |
feign.compression.response.enabled | false | 使来自 Feign 的响应能够被压缩。 |
feign.compression.response.useGzipDecoder | false | 启用要使用的默认 gzip 解码器。 |
feign.encoder.charset-from-content-type | false | 指示字符集是否应从 {@code Content-Type} 标头派生。 |
feign.httpclient.connection-timeout | 2000 | |
feign.httpclient.connection-timer-repeat | 3000 | |
feign.httpclient.disable-ssl-validation | false | |
feign.httpclient.enabled | true | 允许 Feign 使用 Apache HTTP 客户端。 |
feign.httpclient.follow-redirects | true | |
feign.httpclient.hc5.enabled | false | 允许 Feign 使用 Apache HTTP Client 5。 |
feign.httpclient.hc5.pool-concurrency-policy | 池并发策略。 | |
feign.httpclient.hc5.pool-reuse-policy | 池连接重用策略。 | |
feign.httpclient.hc5.socket-timeout | 5 | Socket超时的默认值。 |
feign.httpclient.hc5.socket-timeout-unit | Socket超时单位的默认值。 | |
feign.httpclient.max-connections | 200 | |
feign.httpclient.max-connections-per-route | 50 | |
feign.httpclient.time-to-live | 900 | |
feign.httpclient.time-to-live-unit | ||
feign.metrics.enabled | true | 为 Feign 启用指标功能。 |
feign.okhttp.enabled | false | 允许 Feign 使用 OK HTTP Client。 |
参考:https://docs.spring.io/spring-cloud-openfeign/docs/current/reference/html/