OpenFeign高级特性
# OpenFeign高级特性
# 日志配置
在默认的情况下,我们通过OpenFeign去调用远程的接口的时候,它是没有任何的日志输出的。但是在我们日常开发过程当中,有可能我希望调试一下,我希望得到这个feign,它调用的到底是哪一个服务,他的url到底是什么,他的一些请求头,一些响应的数据,我都希望看一下。
这个时候,我们就可以通过去修改它,默认的日志级别,就可以显示出来。OpenFeign它提供了4种日志级别,默认的日志级别是不显示。
# 配置方式
我们可以通过配置一个Bean的方式来修改它默认的配置。
它的配置方式有两种,一种就是全局配置,我们可以针对所有的服务者,服务提供方来进行配置。另外一种就是局部配置,我可以只针对某一个服务来进行配置。
要进行全局配置的话,我们需要配置一个配置类。
局部配置除了通过配置类的方式,我们还可以通过配置文件的方式来完成局部配置。
# 日志级别
以下是 OpenFeign 的四种日志级别:
- NONE(默认值):不记录任何信息。这是默认设置,适用于生产环境以减少不必要的日志输出。
- BASIC:记录请求方法、URL、响应状态代码以及执行时间。适合需要了解基本通信情况但不需要详细数据流的情况。
- HEADERS:除了 BASIC 级别包含的信息外,还会记录请求和响应的头信息。这对于调试涉及复杂头部操作的问题非常有用。
- FULL:最详细的日志级别,记录包括请求和响应头、正文以及元数据在内的所有信息。非常适合用于深入调试和分析 API 调用过程中的问题。
# 全局配置
实现全局配置,需要定义一个Bean,通过该Bean实现修改默认配置的目的。
@Configuration
public class FeignLogConfig {
@Bean
public Logger.Level feignLoggerLevel() {
// 设置为 FULL 日志级别
return Logger.Level.FULL;
}
}
2
3
4
5
6
7
8
9
到此,修改OpenFeign的日志级别的工作就完成了。
但你此时去访问,可能仍不会输出Feign的对应日志。因为我们这个Feign它的这个调试日志,它是以Debug,级别来输出的。我们都知道spring boot它有一个默认的日志级别,是info级别。
那么info级别它是要大于debug的,所以说debug的这个日志它是不会输出。
一个问题:为什么设置成
DEBUG
就能输出?
- 日志框架过滤机制:日志框架有一个全局的日志级别设置,只有当某个日志消息的级别高于或等于全局设置的日志级别时,该消息才会被记录下来。如果全局的日志级别被设置为
INFO
或更高(如WARN
,ERROR
),那么所有低于此级别的日志(包括DEBUG
和TRACE
)都不会被记录。- 包级别的日志级别控制:除了全局的日志级别外,你还可以针对特定的包或者类设置日志级别。例如,在Spring Boot应用中,如果你希望看到关于某个特定Feign客户端的详细日志,你需要确保该客户端所在包的日志级别至少为
DEBUG
。否则,即使Feign客户端本身配置为Logger.Level.FULL
,如果包的日志级别不够详细,这些日志也不会显示。
此时,你需要的就是修改日志级别,比如:
logging:
level:
com.your.package: DEBUG # 替换为你的Feign客户端所在包的实际路径
# 或者 只设定一个 类
# level:
# com.sugar.order.feign.FeignService: debug
2
3
4
5
6
下面是一些常见的日志级别以及它们的含义:
- TRACE:输出更详细的跟踪信息,通常用于开发和调试阶段。
- DEBUG:输出调试信息,通常用于开发和调试阶段。
- INFO:输出重要的运行信息,通常是应用程序的运行状态和关键事件。
- WARN:输出警告信息,表示可能的问题但不是严重错误。
- ERROR:输出错误信息,表示发生了错误或异常。
# 局部配置
下面是通过,Bean的方式实现局部配置。
如果你只想对几个特定的Feign客户端启用或调整日志级别,而不影响其他Feign客户端,则不需要为这些配置添加@Configuration
注解。相反,你可以直接将配置类传递给特定的Feign客户端,通过@FeignClient
中的configuration
属性来指定。这是因为在这种情况下,你并不是要让整个应用程序上下文都知晓这些配置,而只是想让特定的Feign客户端使用它们。
public class SpecificFeignConfig {
@Bean
Logger.Level feignLoggerLevel() {
return Logger.Level.FULL;
}
}
@FeignClient(name = "specific-service", configuration = SpecificFeignConfig.class)
public interface SpecificFeignClient {
@GetMapping("/api/resource")
String getResource();
}
2
3
4
5
6
7
8
9
10
11
12
13
局部配置还需要在声明式接口配置configuration。
- 配置文件的方式
feign:
client:
config:
order-service: # 应用于 order-service
loggerLevel: full # 设置Feign的日志级别为full
2
3
4
5
那如果同时用了全局,和局部,谁会生效呢?
优先级:在Spring中,如果某个特定组件有更具体的配置(如通过@FeignClient
的configuration
属性指定),那么这个具体配置会覆盖全局配置。这样可以在不影响其他服务的情况下对某些服务进行特殊处理。
# 集成负载均衡
在 Spring Cloud 2020.0.0 及以上版本中,OpenFeign 默认集成了 Spring Cloud LoadBalancer,因此你不需要额外引入 spring-cloud-starter-netflix-ribbon
依赖。但是,为了确保所有必要的依赖项都存在,你仍然需要引入 spring-cloud-starter-loadbalancer
依赖。
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>
2
3
4
5
6
7
8
之后,按照前文的操作,
- 在主类或配置类(消费端)中启用 OpenFeign,即添加@EnableFeignClients注解
- 定义一个接口,并使用
@FeignClient
注解标记该接口为 Feign 客户端 - 在
application.yml
文件中配置服务发现组件 - 在服务中注入并使用 Feign 客户端
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class YourService {
@Autowired
private UserServiceClient userServiceClient;
public User getUserById(Long id) {
return userServiceClient.getUser(id);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
# 超时控制
在Spring Cloud微服务架构中,大部分公司都是利用OpenFeign进行服务间的调用,而比较简单的业务使用默认配置是不会有多大问题的,但是如果是业务比较复杂,服务要进行比较繁杂的业务计算,那后台很有可能会出现Read Timeout这个异常,因此定制化配置超时时间就有必要了。
OpenFeign默认等待60秒钟,超过后报错
默认OpenFeign客户端等待60秒钟,但是服务端处理超过规定时间会导致Feign客户端返回报错。
为了避免这样的情况,有时候我们需要设置Feign客户端的超时控制,默认60秒太长或者业务时间太短都不好
yml文件中开启配置:
connectTimeout 连接超时时间
readTimeout 请求处理超时时间
2
官网对应介绍-默认属性 (opens new window)
- https://docs.spring.io/spring-cloud-openfeign/docs/current/reference/html/#spring-cloud-feign-overriding-defaults
以下是2个时间的区别:
connectTimeout
控制的是从客户端发起连接请求到成功建立TCP连接这段时间的最大等待值。它主要用于解决网络不可达的问题。readTimeout
控制的是从成功建立连接后到接收到完整响应这段时间的最大等待值。它主要用于解决服务器响应缓慢的问题。
# 配置
spring:
cloud:
openfeign:
client:
config:
default:
#连接超时时间
connectTimeout: 3000
#读取超时时间
readTimeout: 3000
2
3
4
5
6
7
8
9
10
使用default表示全局配置,任何一处都是如此。当然也可以指定配置。
spring:
cloud:
openfeign:
client:
config:
default: # 表示全局配置
#连接超时时间
connectTimeout: 3000
#读取超时时间
readTimeout: 3000
serviceC:
#连接超时时间
connectTimeout: 2000
#读取超时时间
readTimeout: 2000
2
3
4
5
6
7
8
9
10
11
12
13
14
15
加上服务名后就表示只有该服务是如此。当然,全局配置和单一配置可以共存。此时,单一配置会覆盖全局配置,当然其他服务仍是default的值。
# 配置类配置
除了通过配置文件,你还可以通过创建一个自定义的配置类,并将其应用到特定的Feign客户端上来设置超时时间。这通常涉及到Feign.Builder
的定制化。
首先,添加必要的依赖,确保你的项目包含对spring-cloud-starter-openfeign
的支持。
配置类:
import feign.Request;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class FeignConfig {
@Bean
public Request.Options options() {
// 设置连接超时时间为5秒,读取超时时间为5秒
return new Request.Options(5000, 5000);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
OpenFeign客户端类:
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
@FeignClient(name = "myFeignClient", configuration = FeignConfig.class)
public interface MyFeignClient {
@GetMapping("/api/resource")
String getResource();
}
2
3
4
5
6
7
8
9
# 重试机制
OpenFeign的重试,默认重试是关闭的,给了默认值。
开启Retryer功能
新增配置类FeignConfig并修改Retryer配置
package com.atguigu.cloud.config;
import feign.Retryer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @auther zzyy
* @create 2023-11-10 11:09
*/
@Configuration
public class FeignConfig
{
@Bean
public Retryer myRetryer()
{
//return Retryer.NEVER_RETRY; //Feign默认配置是不走重试策略的
//最大请求次数为3(1+2),初始间隔时间为100ms,重试间最大间隔时间为1s
return new Retryer.Default(100,1,3);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 修改默认HttpClient
OpenFeign中http client 如果不做特殊配置,OpenFeign默认使用JDK自带的HttpURLConnection发送HTTP请求,
由于默认HttpURLConnection没有连接池、性能和效率比较低,如果采用默认,性能上不是最牛B的,所以加到最大。
修改对应的pom
<!-- httpclient5-->
<dependency>
<groupId>org.apache.httpcomponents.client5</groupId>
<artifactId>httpclient5</artifactId>
<version>5.3</version>
</dependency>
<!-- feign-hc5-->
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-hc5</artifactId>
<version>13.1</version>
</dependency>
2
3
4
5
6
7
8
9
10
11
12
Apache HttpClient5 配置开启说明
# Apache HttpClient5 配置开启
spring:
cloud:
openfeign:
httpclient:
hc5:
enabled: true
2
3
4
5
6
7
# 请求/响应压缩
对请求和响应进行GZIP压缩
Spring Cloud OpenFeign支持对请求和响应进行GZIP压缩,以减少通信过程中的性能损耗。
通过下面的两个参数设置,就能开启请求与相应的压缩功能:
spring.cloud.openfeign.compression.request.enabled=true
spring.cloud.openfeign.compression.response.enabled=true
细粒度化设置
对请求压缩做一些更细致的设置,比如下面的配置内容指定压缩的请求数据类型并设置了请求压缩的大小下限,
只有超过这个大小的请求才会进行压缩:
spring.cloud.openfeign.compression.request.enabled=true
spring.cloud.openfeign.compression.request.mime-types=text/xml,application/xml,application/json #触发压缩数据类型
spring.cloud.openfeign.compression.request.min-request-size=2048 #最小触发压缩的大小
server:
port: 80
spring:
application:
name: cloud-consumer-openfeign-order
####Spring Cloud Consul for Service Discovery
cloud:
consul:
host: localhost
port: 8500
discovery:
prefer-ip-address: true #优先使用服务ip进行注册
service-name: ${spring.application.name}
openfeign:
client:
config:
default:
#cloud-payment-service:
#连接超时时间
connectTimeout: 4000
#读取超时时间
readTimeout: 4000
httpclient:
hc5:
enabled: true
compression:
request:
enabled: true
min-request-size: 2048 #最小触发压缩的大小
mime-types: text/xml,application/xml,application/json #触发压缩数据类型
response:
enabled: true
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
# 契约配置
什么叫契约配置呢?就是将我们之前OpenFeign接口当中的这些springMvc的注解,给它还原成Feign的原生的注解。
为什么要实现这个功能呢?为了向下兼容。
比方说我们以前spring cloud一的早期版本,它其实就是用的原生的Feign。那么随着next netflix停更,才替换成了OpenFeign。
假如说我现在有个项目,它就是用的spring clode早期的一个版本。我现在想做版本的一个升级,我想升级比较新的一个版本,我想用OpenFeign。但老项目只支持原生注解,此时——契约配置就可以登场了。
# 自定义拦截器
它也针对拦截器所做的一些扩展。
注意:这里的拦截指的是 服务端中
——消费端调用提供端的这个过程。
spring MVC的拦截器它是在我们的客户端发送请求到服务端,起的作用,这是spring VC的拦截器。
那么OpenFeign的拦截器,它是当我们的这个消费端去调用我们的服务提供端的时候起的作用。
那它的作用有哪些呢?比如:在服务端去调用提供端的时候,每一次都去记录日志。我觉得你的日志我不满意,我自己去记录一些我自己的日志。还比方说我们服务端去调提供端的时候,我每一次请求,我都给你去带一些个参数。或者说我在请求头当中,我可以给你去设置一些参数。我们就可以做一些认证授权对不对。
# 实现自定义类
你需要创建一个类实现RequestInterceptor
接口,并重写其apply
方法。在这个方法中,你可以访问并修改即将发出的请求。
import feign.RequestInterceptor;
import feign.RequestTemplate;
public class CustomFeignInterceptor implements RequestInterceptor {
@Override
public void apply(RequestTemplate template) {
// 在这里可以对请求进行修改
// 比如添加请求头
template.header("Custom-Header", "CustomValue");
// 或者根据需要添加其他逻辑
String method = template.method();
if ("GET".equalsIgnoreCase(method)) {
template.query("api-version", "1.0");
}
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
接下来,你需要将自定义的拦截器注册到Spring上下文中。这可以通过创建一个配置类并在其中定义拦截器作为bean来完成。
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class FeignConfig {
@Bean
public RequestInterceptor customFeignInterceptor() {
return new CustomFeignInterceptor();
}
}
2
3
4
5
6
7
8
9
10
11
确保你的Feign客户端使用了这个配置类。你可以在声明Feign客户端时指定这个配置类:
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
@FeignClient(name = "example-client", url = "http://example.com", configuration = FeignConfig.class)
public interface ExampleClient {
@GetMapping("/api/resource")
String getResource();
}
2
3
4
5
6
7
8
9
上述的声明式客户端类,多了一个属性url。它起什么作用呢?
在@FeignClient
注解中指定的url
属性用于直接提供服务的基本URL。这个设置通常用于以下几种场景:
- 外部服务调用:当你需要调用不在Spring Cloud服务发现机制中的外部HTTP服务时,可以直接通过
url
属性指定目标服务的完整地址。例如,如果你想要调用一个公开的REST API,而这个API并不由你的微服务架构管理或者不支持服务发现,这时就可以使用url
属性。 - 开发和测试阶段:在开发或测试环境中,可能还没有配置好服务发现(如Eureka, Consul等),或者你希望快速地对某个服务进行本地调试,这时可以通过
url
属性直接指向该服务的运行实例。 - 固定服务地址:对于某些特定的服务,如果其地址是固定的,并且不需要动态发现或负载均衡,也可以直接通过
url
属性来指定。