一、OpenFeign简介
概述:feign是一个声明式的http客户端其作用就是帮助我们优雅的实现http请求的发送。官方地址
二、 入门使用
1、 在消费者的一方引入依赖
<!-- 加入OpenFeign的依赖 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
2、在启动类上添加**@EnableFeignClients**开启OpenFeign的功能支持
3、编写OpenFeign的客户端
@FeignClient(value="spzx-cloud-user")
// spzx-cloud-user 是服务的名称,它是注册在服务注册中心(我用的是Nacos) 其余的例如 Eureka 或 Consul中的服务名。Feign 会使用该服务名来进行服务发现和路由
//一个属性value可以省略
public interface UserFeignClient {
@GetMapping("/api/user/selectByUserId/{userId}")
User selectById(@PathVariable Long userId);
}
这个客户端主要是基于SpringMVC的注解来声明远程调用的信息,比如:
① 请求方式:GET
② 请求路径:/api/user/selectByOrderId/{userId}
③ 请求参数:Long userId
④ 返回值类型:User
这样,Feign就可以帮助我们发送http请求,无需自己使用RestTemplate来发送了。
@Service
public class OrderServiceImpl implements OrderService {
@Autowired
private OrderMapper orderMapper;
@Autowired
private UserFeignClient userFeignClient;
//使用feign 可以更简便的发请求, 代替restTemplate
@Override
public Order selectByOrderId(Long orderId) {
// 根据orderId查询订单信息
Order order = orderMapper.selectByOrderId(orderId);
// 发送请求, 请求user微服务模块
User user = userFeignClient.selectById(order.getUserId());
order.setUser(user);
return order;
}
}
#OpenFeign自定义配置
日志配置
OpenFeign可以支持很多的自定义配置,如下表所示:
类型 | 作用 | 说明 |
---|---|---|
feign.Logger.Level | 修改日志级别 | 包含四种不同的级别:NONE、BASIC、HEADERS、FULL |
feign.codec.Decoder | 响应结果的解析器 | http远程调用的结果做解析,例如解析json字符串为java对象 |
feign.codec.Encoder | 请求参数编码 | 将请求参数编码,便于通过http请求发送 |
feign.Contract | 支持的注解格式 | 默认是SpringMVC的注解 |
feign.Retryer | 失败重试机制 | 请求失败的重试机制,默认是没有,不过会使用Ribbon的重试 |
一般情况下,默认值就能满足我们使用,如果要自定义时,只需要创建自定义的@Bean覆盖默认Bean即可。
下面以日志为例来演示如何自定义配置,支持两种方式的配置:
基于配置文件的方式
基于配置文件修改feign的日志级别可以针对单个服务:
# 将feign包下产生的日志的级别设置为debug
# spring的日志级别方是info
# ERROR > WARN > INFO > DEBUG > TRACE 设置的级别只有比当前级别更高才会输出
logging:
level:
com.chen.spzx.cloud.order.feign: debug
也可以针对所有服务:
# openfeign日志级别配置
spring:
cloud:
openfeign:
client:
config:
default: # 这里用default就是全局配置,如果是写服务名称,则是针对某个微服务的配置
loggerLevel: full
而日志的级别分为四种:
① NONE:不记录任何日志信息,这是默认值。
② BASIC:仅记录请求的方法,URL以及响应状态码和执行时间
③ HEADERS:在BASIC的基础上,额外记录了请求和响应的头信息
④ FULL:记录所有请求和响应的明细,包括头信息、请求体、元数据。
-------上面是一组方式, 下面是一组方式---------
Java代码的方式
也可以基于Java代码来修改日志级别,先声明一个类,然后声明一个Logger.Level的对象:
@Configuration
public class DefaultFeignConfiguration {
@Bean
public Logger.Level feignLogLevel(){
return Logger.Level.BASIC; // 日志级别为BASIC
}
}
如果要全局生效,将其放到启动类的@EnableFeignClients这个注解中:
@EnableFeignClients(defaultConfiguration = DefaultFeignConfiguration .class)
如果是局部生效,则把它放到对应的@FeignClient这个注解中:
@FeignClient(value = "spzx-cloud-user", configuration = DefaultFeignConfiguration .class) //用的是 configuration 这个属性
注意需要指定feign包的配置
logging:
level:
com.atguigu.spzx.cloud.order.client: debug
feign所在的包:com.atguigu.spzx.cloud.order.client
原因:# 因为feign调试日志是debug级别输出,springboot默认的日志级别是info,所以feign的debug日志级别就不会输出
# logging.level=debug这样配置是对所有的日志级别进行配置
# 该场景只需要对feign接口进行debug配置,所以是这样配置logging.level.com.atguigu.spzx.cloud.order.client=debug
超时配置
超时机制概述:Feign 的超时机制是指在使用 Feign 进行服务间的 HTTP 调用时,设置请求的超时时间。当请求超过设定的超时时间后,Feign 将会中断该请求并抛出相应的异常。
spring:
cloud:
openfeign:
client:
config:
default:
loggerLevel: full
read-timeout: 2000 # 读取数据的超时时间设置为2s
connect-timeout: 2000 # 简历连接的超时时间设置为2s
重试配置
feign一旦请求超时了,那么此时就会直接抛出SocketTimeoutException: Read timed out的异常。请求超时的原因有很多种,如网络抖动、服务不可用等。如果由于网络暂时不可用导致触发了超时机制,那么此时直接返回异常信息就并不是特别的合理,尤其针对查询请求,肯定希望得到一个结果。合理的做法:触发超时以后,让feign进行重试。
1、自定义重试器
public class FeignClientRetryer implements Retryer {
// 定义两个成员变量来决定重试次数
private int start = 1 ;
private int end = 3 ;
@Override
public void continueOrPropagate(RetryableException e) {
// 是否需要进行重试取决于该方法是否抛出异常,如果抛出异常重试结束
if(start >= end) {
throw new RuntimeException(e) ;
}
start++ ;
}
@Override
public Retryer clone() {
// 框架底层调用该方法得到一个重试器
return new FeignClientRetryer();
}
}
2、配置重试器
spring:
cloud:
openfeign:
client:
config:
default:
loggerLevel: full
read-timeout: 2000
connect-timeout: 2000
retryer: 这里写类的全限定名 # 配置自定义重试器
三、 开发中把openFeign抽取出来当一个单独的模块
通过将 OpenFeign 抽取成独立模块,可以使系统更加清晰、灵活、易于维护,同时也提高了代码复用性和测试的便利性。这种做法在微服务架构中尤其重要,因为微服务之间的调用复杂且多样化,统一管理和解耦能大大简化系统的复杂性。
新建一个module, 这个module 中写的是Feign的配置和接口. 使用的时候让消费者依赖这个模块 并且消费者还需要在启动类配置包扫描, 确定能够扫到.
@EnableFeignClients(basePackages = {
//扫描指定包及其子包
"com.chen.spzx.cloud.feign"
})
代码只是做了迁移, 写到了单独的模块, 内容都一样. 这里不重复粘贴了