1、OpenFeign的前言
Feign 是一个声明式 WebService客户端。使用Feign能让编写Web Service客户端更加简单。
它的使用方法是 定义一个服务接口然后在上面添加注解。Feign也支持可拔插式的编码器和解码器。SpringCloud对Feign进行了封装,使其支持了SpringMVC标准注解和 HttpMessageConverters。Feign可以与Eureka和Ribbon组合使用以支持负载均衡。
1.1、Feign的作用
Feign 旨在使编写 Java Http客户端变得更容易
。
前面在使用Ribbon+RestTemplate时,利用RestTemplate对Http请求的封装处理,形成了一套模板化的调用方法。但是在实际开发中,由于对服务依赖的调用可能不止一处,往往一个接口会被多处调用,所有通常都会针对每个微服务自行封装一些客户端类来包装这些依赖服务的调用
。所以,Feign在此基础上作了进一步封装,由他来帮助我们定义和实现依赖服务接口的定义。在Feign的实现下,只需创建一个接口并使用注解的方式来配置它(以前是Dao层接口上面的标注@Mapper注册,现在是一个为服务接口上面标注一个Fegin注解即可)
,即可完成对服务提供方的接口绑定,简化了使用Spring Cloud Ribbon时,自动封装服务调用客户端的开发量。
Feign集成了Ribbon
利用Ribbon维护了Payment服务列表信息,并且通过轮询实现了客户端的负载均衡。而与Ribbon不同的是,通过Feign只需要自定义服务绑定接口且以声明式的方法
,优雅而简单的实现了服务调用。
1.2、Feign和OpenFeign的区别
Feign | OpenFeign |
---|---|
Feign是SpringCloud组件中的一个轻量级RestFul的HTTP服务客户端,Feign内置了Ribbon,用来做客户端负载均衡,去调用服务注册中心的服务 。Feign的使用方式是:使用Feign的注解定义接口,调用这个接口,就可以调用服务注册中心的服务 | OpenFeign是SpringCloud在Feign的基础上支持了SpringMVC的注解。如@ResuqsetMapping等等。OpenFeign的@FeignClient注解可以解析SpringMVC的@ResqusetMapping注解下的接口,并通过动态代理的方式产生实现类 ,实现类中做负载均衡并调用其他服务。 |
org.springframework.cloud spring-cloud-starter-feign | org.springframework.cloud spring-cloud-starter-openfeign |
2、OpenFeign的使用步骤
2.1、新建一个CLOUD-CONSUMER-FEIGN-ORDER80模块
2.1.1、配置application.yml
#服务的端口号
server:
port: 80
#服务的名字
spring:
application:
name: cloud-feign-order-service
eureka:
client:
#由于是客户端,可以不注册进Eurek-Server中
register-with-eureka: false
service-url:
#集群版的服务中心
defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka
2.1.2、编写主启动类
/**
* @author success
* @version 1.0
* @date 2020/7/10 10:34
* 使用 @EnableFeignClients 注解开启OpenFeign的功能
*/
@SpringBootApplication
@EnableFeignClients
public class OrderFeignMain80 {
public static void main(String[] args) {
SpringApplication.run(OrderFeignMain80.class,args);
}
}
2.1.3、编写业务类
/**
* 创建一个带有@FeignClient的注解,用来对服务提供方的接口绑定
* 在服务提供者的server层上的接口,有一个就绑定一个,有两个绑定两个方法
* @author success
* @version 1.0
* @date 2020/7/10 10:39
*/
@Component
@FeignClient(value = "CLOUD-PAYMENT-SERVICE")
public interface PaymentFeignService {
@GetMapping(value = "/payment/create")
CommonResult<Payment> create(Payment payment);
/**
* 根据具体的id进行查询
* @param id id
* @return
*/
@GetMapping(value = "/payment/get/{id}")
CommonResult<Payment> getPaymentById(@PathVariable("id") long id);
}
2.1.4、编写controller层的OrderFeignController类
/**
* @author success
* @version 1.0
* @date 2020/7/10 10:54
*/
@RestController
public class OrderFeignController {
@Resource
private PaymentFeignService paymentFeignService;
@GetMapping(value = "/payment/get/{id}")
public CommonResult<Payment> getPaymentById(@PathVariable("id")long id){
return paymentFeignService.getPaymentById(id);
}
}
2.1.5、Feign 与 Ribbon的区别
由于 Ribbon 和 Feign 都是用于实现软负载均衡的组件,Ribbon 和 Feign都是用于调用其他服务提供者的,但是方式不同。
2.1.5.1、Ribbon调用服务的方式
-
是通过 RestTemplate类发送HTTP请求的封装
从而获取到需要的服务<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency>
由于 Eureka-Client里面包含了Ribbon的依赖,所以只引入Eureka-Client这个依赖就可以了。
/**
*1、 先声明一个 restTemplate的实例
*2、再
*/
@Resource
private RestTemplate restTemplate;
/**
* 使用 restTemplate 返回 json字符串的形式
* 消费者服务调用订单服务
* @param id
* @return
*/
@GetMapping(value = "/consumer/payment/get/{id}")
public CommonResult<Payment> getPaymentById(@PathVariable("id")long id){
return restTemplate.getForObject(PAYMENT_URL+"/payment/get/"+id,CommonResult.class);
}
-
再构建一个 http请求,模拟请求之后使用RestTemplate发送给其他服务
-
Ribbon 使用的注解是 @RibbonClient
2.1.5.2、Feign的方式
Feign是基于 Ribbon组件进行改进得到的。所以Feign本身就包含了Ribbon组件。
方式就在上面。
2.1.5.3、总结
- 由于使用Ribbon组件实现软负载均衡的话,一个服务提供者的方法会在消费者端进行多出调用,从而使代码的耦合性变高。
- 而Feign组件 是将需要调用的服务提供者的server层的方法在客户端server层进行封装。从而达到降低代码的耦合性的目的。
- 两者之间的启动用的注解不同:
- Ribbon使用的是@RibbonClient注解。
- Feign使用的是@EnableFeignClients注解。
- 服务指定的位置不同:
- Ribbon 是在@RibbonClient注解上声明。
- Feign 是在@FeignClient注解上声明。
- 调用方式不同:
- Ribbon需要自己构建http请求,在通过RestTemplate类将请求封装后发送给其他服务,非常的繁琐。
- Feign采用接口的方式,将需要调用其他服务的方法定义成接口,不需要构建自己的HTTP请求。不过需要要主义的是:
- feign在第一次调用相应的server的接口时,会出现因超时而导致的失败,所以需要设置超长的请求时间。
- 构建的抽象方法时,service使用的注解,方法名和提供的方法完全一致。
2.3、OpenFeign的超时控制
在客户端中,调用服务提供者的服务时,因为业务的需求会处理的很慢,但是在服务提供者这边时明确了多少时间之内的可以提供服务的。而在客户端这边由于使用的时Feign组件实现调用服务的,当出现业务需求很繁杂的时候,会出现上面说的请求超时。
如下所示:
@GetMapping("/payment/time/out/{id}")
public CommonResult getPayment_Time_Out(@PathVariable("id") long id){
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Payment paymentById = paymentService.getPaymentById(id);
if(paymentById != null) {
return new CommonResult(200, "数据查询成功" + serverPort, paymentById);
}else{
return new CommonResult(400,"数据查询失败",null);
}
}
当出现线程睡眠或者别的业务量非常大的时候,feign客户端去调用时,就会出错。
解决方法:可以在 yml文件中配置请求时长
#设置 feign客户端超时时间(OpenFeign默认支持Ribbon)
ribbon:
#指的是建立连接所需要的时间,适用于网络状况正常的情况下,两端连接所有的时间
ReadTimeout: 5000
#指的是建立连接之后服务器读取到可用资源的时间
ConnectTimeout: 5000
OpenFeign默认的请求时间为 :1秒
2.4、OpenFeign日志打印功能
Feign提供了日志打印功能,我们可以通过配置来调整日志级别,从而了解Feign 对 Http请求的细节。
就是对Feign接口的调用情况进行监控和输出
2.4.1、日志级别
- NONE:默认的,不显示任何日志
- BASIC:仅记录请求方法、URL、响应状态码执行时间
- HEADERS:除了BASIC中定义的信息之外,还有请求和响应的头信息
- FULL(最详细的):除了HEADERS中定义的信息之外,还有请求和响应的正文及元数据
2.4.2、使用步骤
2.4.2.1、配置日志Bean
/**
* 配置 Feign的日志打印功能
* 开启详细日志的打印
* @author success
* @version 1.0
* @date 2020/7/10 16:05
*/
@Configuration
public class FeignConfig {
/**
* 使用日志级别最全的
* @return
*/
@Bean
Logger.Level feignLoggerLevel(){
return Logger.Level.FULL;
}
}
2.4.2.2、在YML文件里开启日志的Feign客户端
logging:
level:
#声明:日志以什么级别监控那个接口
#以DeBug的形式打印 FULL级别的日志
cn.success.springcloud.service.PaymentFeignService: debug
模拟之后,控制台打印的日志