OpenFegin
约 2405 字大约 8 分钟
2025-02-27
我们以前利用RestTemplate发起远程调用的代码
@GetMapping("/get/{id}")
public Order getOrderById(@PathVariable int id){
Order order = orderService.getById(id);
String url = "http://userService/user/get/"+order.getUserId();
User user = restTemplate.getForObject(url, User.class);
order.setUser(user);
return order;
}
存在下面的问题:
- 代码可读性差,编程体验不统一
- 参数复杂URL难以维护
Feign是一个声明式的http客户端,官方地址:https://github.com/OpenFeign/feign
其作用就是帮助我们优雅的实现 http 请求的发送,解决上面提到的问题
1. OpenFeign概述
Feign是一个声明式的http客户端,它使得写http客户端变得更简单,只需要创建一个接口并注解。是SpringCloud微服务架构中用来服务远程调用的组件。
cloud官网介绍Feign:https://docs.spring.io/spring-cloud-openfeign/docs/current/reference/html/
OpenFeign源码:https://github.com/OpenFeign/feign
2. OpenFeign能干嘛?
Java当中常见的Http客户端有很多,除了Feign,类似的还有Apache 的 HttpClient 以及OKHttp3,还有SpringBoot自带的RestTemplate
这些都是Java当中常用的HTTP 请求工具。
- 支持SpringMVCzhi的注解,使得编写接口更加简单,不用再写一堆的xml配置。
- 支持负载均衡,Feign内置了Ribbon的负载均衡算法,可以自动选择合适的服务实例。
- 支持Hystrix,提供熔断机制,避免服务雪崩。
3. OpenFeign和RestTemplate的区别
- RestTemplate是Spring提供的用于访问Restful服务的客户端模板类,它提供了一系列便捷的方法用来访问REST服务,比如GET、POST等。
- Feign是SpringCloud提供的声明式的HTTP客户端,它使得编写Java接口变得更简单,只需要创建一个接口并注解,就可以调用HTTP服务。Feign集成了Ribbon,实现了负载均衡。
- Feign和RestTemplate的区别主要在于:
- Feign是声明式的,使用注解来定义HTTP请求,而RestTemplate是基于代码的,需要手动拼接HTTP请求。
- Feign支持SpringMVC的注解,比如@GetMapping、@PostMapping等,而RestTemplate没有提供类似的注解。
- Feign默认集成了Ribbon,可以自动选择合适的服务实例,而RestTemplate需要手动配置。
- Feign默认实现了Hystrix,提供熔断机制,避免服务雪崩。
4. Fegin和OpenFeign的区别
- Feign是Spring Cloud Netflix项目下的子项目,是一个声明式的HTTP客户端,它使得编写Java接口变得更简单,只需要创建一个接口并注解,就可以调用HTTP服务。Feign集成了Ribbon,实现了负载均衡。
- OpenFeign是Spring Cloud Alibaba项目下的子项目,它是Feign的增强版本
Feign是在2019就已经不再更新了,通过maven网站就可以看出来,随之取代的是OpenFeign,从名字上就可以知道,他是Feign的升级版
5. Feign的使用
微服务之间使用OpenFeign,肯定是要通过注册中心来访问服务的。提供者将自己的ip+端口号注册到注册中心,然后对外提供一个服务名称,消费者根据服务名称去注册中心当中寻找ip和端口。
5.1 创建一个接口模块
创建一个pms-api 模块专门用于提供feign接口
该接口是对pms-boot服务中接口的对外提供
5.2 导入依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
5.3 创建Feign接口
@FeignClient(value = "pms-service")
public interface PmsFeignClient {
@GetMapping("pms/get/{id}")
public String getPms(@PathVariable("id") Long id);
}
5.4 使用Feign接口(在服务消费者引入该模块)
在服务消费者中导入该依赖
<dependency>
<groupId>com.syh</groupId>
<artifactId>pms-api</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
在启动类上添加注解@EnableFeignClients(basePackages = "com.syh.pms.api"),开启Feign客户端功能
@SpringBootApplication
@EnableFeignClients(basePackageClasses = {PmsFeignClient.class})
public class OmsApplication {
public static void main(String[] args) {
SpringApplication.run(OmsApplication.class, args);
}
}
注意: 当定义的 FeignClient 不在 SpringBootApplication 的扫描包范围下时,这些 FeignClient 就不能使用。修改 order-service 启动类上的 @EnableFeignClients 注解
方式1:指定Feign应该扫描的包
@EnableFeignClients(basePackages = "com.syh.oms.api")
方式2:指定需要加载的Client接口类
@EnableFeignClients(basePackageClasses = {PmsFeignClient.class})
方式3:
@EnableFeignClients(clients = {PmsFeignClient.class})
5.5 调用Feign接口
@RestController
public class OrderController {
@Autowired
private PmsFeignClient pmsFeignClient;
@GetMapping("/order/get/{id}")
public Order getOrderById(@PathVariable int id){
//调用Feign接口
String pms = pmsFeignClient.getPms(order.getPmsId());
return order;
}
}
6. @FeignClient注解详解
- value:指定注册中心中注册的服务名称,这里是pms-service。
- url:指定服务的地址,Feign会向这个地址发送请求。
- fallback:指定熔断器的类,当服务调用失败时,会调用熔断器的类来处理。
- path:指定服务的请求前缀,比如服务的请求路径是/user/get,而我们只想调用/get接口,就可以使用path属性。
- decode404:当服务返回404时,是否抛出异常,默认为false。
- contract:指定Feign的契约,Feign默认使用SpringMvcContract,可以指定其他的契约,比如SpringCloudContract。
- configuration:指定Feign的配置类,Feign默认配置了Ribbon负载均衡,可以指定其他的配置类。
- headers:指定Feign的请求头,Feign默认会添加Content-Type、Accept、Accept-Encoding、User-Agent请求头。
- queryMapEncoded:是否对请求参数进行URL编码,默认为false。
- retryer:指定Feign的重试策略,Feign默认使用Feign的重试策略,可以指定其他的重试策略。
- logLevel:指定Feign的日志级别,Feign默认使用NONE级别,可以指定DEBUG、INFO、WARNING、ERROR级别。
- decode404:当服务返回404时,是否抛出异常,默认为false。
- fallbackFactory:指定熔断器工厂类,Feign默认使用Feign的默认熔断器工厂类,可以指定自己的熔断器工厂类。
- requestInterceptors:指定Feign的请求拦截器,Feign默认不拦截请求,可以指定自己的请求拦截器。
- responseInterceptors:指定Feign的响应拦截器,Feign默认不拦截响应,可以指定自己的响应拦截器。
7. Feign的超时设置
默认OpenFeign客户端等待60秒钟,但是服务端处理超过规定时间会导致Feign客户端返回报错。
yml文件对应配置:
- connectTimeout:连接超时时间
- readTimeout:请求处理超时时间
如果超时就会报错
java.net.SocketTimeoutException: Read timed out
解决方法: 在yml文件中配置超时时间
spring:
cloud:
openfeign:
client:
config:
default:
connect-timeout: 2000 # 连接超时时间
read-timeout: 2000 # 读取超时时间
在这个配置中,connectTimeout
是连接超时时间(单位为毫秒),readTimeout
是读取超时时间(单位为毫秒)。default 是针对所有 Feign 客户端的默认配置。你也可以通过指定服务名称来为特定的服务配置超时时间。
也可以在feign客户端添加配置类的方式来设置超时时间
public class FeignConfig {
@Bean
public Request.Options feignOptions() {
return new Request.Options(2000, 2000); // 连接超时2秒,读取超时2秒
}
}
在feign接口中添加配置属性
@FeignClient(value = "pms-service",configuration = {FeignConfig.class})
public interface PmsFeignClient {
@GetMapping("pms/get/{id}")
public String getPms(@PathVariable("id") Long id);
}
测试: 在feign接口的方法中默认超时
@GetMapping("/get/{id}")
public String getPms(@PathVariable("id") Long id) {
//暂停几秒钟线程
try {
TimeUnit.SECONDS.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "PMS"+id;
}
那么请求该方式2秒后即报错
8. 重试机制
当我们报错,希望自动多次请求重试时,步骤如下
- 配置类
public class FeignConfig {
@Bean
public Request.Options feignOptions() {
return new Request.Options(2000, 2000); // 连接超时2秒,读取超时2秒
}
@Bean
public Retryer myRetryer() {
// return Retryer.NEVER_RETRY;//Feign默认是不走重试策略订单
// 自定义重试策略,重试3次,每次间隔100ms
return new Retryer.Default(100,1,3);
}
}
在feign接口添加配置属性
@FeignClient(value = "pms-service",configuration = {FeignConfig.class})
public interface PmsFeignClient {
@GetMapping("pms/get/{id}")
public String getPms(@PathVariable("id") Long id);
}
模拟超时报错
我设置的超时时间为2秒
@GetMapping("/get/{id}")
public String getPms(@PathVariable("id") Long id) {
System.out.println("开始时间:"+new Date());
//暂停几秒钟线程
try {
TimeUnit.SECONDS.sleep(60);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
System.out.println("结束时间:"+new Date());
}
return "PMS"+id;
}
控制台打印信息
开始时间:Wed Apr 17 19:55:52 CST 2024
开始时间:Wed Apr 17 19:55:54 CST 2024
开始时间:Wed Apr 17 19:55:56 CST 2024
结束时间:Wed Apr 17 19:56:52 CST 2024
结束时间:Wed Apr 17 19:56:54 CST 2024
结束时间:Wed Apr 17 19:56:56 CST 2024
可以看到,请求超时了3次
9. 日志打印
Feign 提供了日志打印功能,我们可以通过配置来调整日志级别,从而了解 Feign 中 Http 请求的细节。 说白了就是对Feign接口的调用情况进行监控和输出
日志级别:
- NONE:默认的,不显示任何日志;
- BASIC:仅记录请求方法、URL、响应状态码及执行时间;
- HEADERS:除了 BASIC 中定义的信息之外,还有请求和响应的头信息;
- FULL:除了 HEADERS 中定义的信息之外,还有请求和响应的正文及元数据。
配置日志级别:
public class FeignConfig {
@Bean
public Request.Options feignOptions() {
return new Request.Options(2000, 2000); // 连接超时2秒,读取超时2秒
}
@Bean
public Retryer myRetryer() {
// return Retryer.NEVER_RETRY;//Feign默认是不走重试策略订单
// 自定义重试策略,重试3次,每次间隔100ms
return new Retryer.Default(100,1,3);
}
@Bean
Logger.Level feignLoggerLevel() {
return Logger.Level.FULL;
}
}
这里还是看不到日志信息:因为我们的服务默认打印的是日志级别为INFO 解决方法: 在yml文件中配置日志级别
logging:
level:
com.syh.oms.api: debug
这里配置了com.syh.oms.api包下的日志级别为debug,所以我们可以看到Feign的日志信息 测试: