其他分享
首页 > 其他分享> > 5.OpenFeign:Spring Cloud声明式服务调用组件

5.OpenFeign:Spring Cloud声明式服务调用组件

作者:互联网

Netflix Feign 是 Netflix 公司发布的一种实现负载均衡和服务调用的开源组件。Spring Cloud 将其与 Netflix 中的其他开源服务组件(例如 Eureka、Ribbon 以及 Hystrix 等)一起整合进 Spring Cloud Netflix 模块中,整合后全称为 Spring Cloud Netflix Feign。
 
Feign 对 Ribbon 进行了集成,利用 Ribbon 维护了一份可用服务清单,并通过 Ribbon 实现了客户端的负载均衡。

Feign 是一种声明式服务调用组件,它在 RestTemplate 的基础上做了进一步的封装。通过 Feign,我们只需要声明一个接口并通过注解进行简单的配置(类似于 Dao 接口上面的 Mapper 注解一样)即可实现对 HTTP 接口的绑定。

通过 Feign,我们可以像调用本地方法一样来调用远程服务,而完全感觉不到这是在进行远程调用。

Feign 支持多种注解,例如 Feign 自带的注解以及 JAX-RS 注解等,但遗憾的是 Feign 本身并不支持 Spring MVC 注解,这无疑会给广大 Spring 用户带来不便。

2019 年 Netflix 公司宣布 Feign 组件正式进入停更维护状态,于是 Spring 官方便推出了一个名为 OpenFeign 的组件作为 Feign 的替代方案。

OpenFeign

OpenFeign 全称 Spring Cloud OpenFeign,它是 Spring 官方推出的一种声明式服务调用与负载均衡组件,它的出现就是为了替代进入停更维护状态的 Feign。

OpenFeign 是 Spring Cloud 对 Feign 的二次封装,它具有 Feign 的所有功能,并在 Feign 的基础上增加了对 Spring MVC 注解的支持,例如 @RequestMapping、@GetMapping 和 @PostMapping 等。

OpenFeign 常用注解

使用 OpenFegin 进行远程服务调用时,常用注解如下表。 

注解 说明
@FeignClient  该注解用于通知 OpenFeign 组件对 @RequestMapping 注解下的接口进行解析,并通过动态代理的方式产生实现类,实现负载均衡和服务调用。
@EnableFeignClients 该注解用于开启 OpenFeign 功能,当 Spring Cloud 应用启动时,OpenFeign 会扫描标有 @FeignClient 注解的接口,生成代理并注册到 Spring 容器中。
@RequestMapping Spring MVC 注解,在 Spring MVC 中使用该注解映射请求,通过它来指定控制器(Controller)可以处理哪些 URL 请求,相当于 Servlet 中 web.xml 的配置。
@GetMapping Spring MVC 注解,用来映射 GET 请求,它是一个组合注解,相当于 @RequestMapping(method = RequestMethod.GET) 。
@PostMapping Spring MVC 注解,用来映射 POST 请求,它是一个组合注解,相当于 @RequestMapping(method = RequestMethod.POST) 。

Spring Cloud Finchley 及以上版本一般使用 OpenFeign 作为其服务调用组件。由于 OpenFeign 是在 2019 年 Feign 停更进入维护后推出的,因此大多数 2019 年及以后的新项目使用的都是 OpenFeign,而 2018 年以前的项目一般使用 Feign。

Feign VS OpenFeign 

下面我们就来对比下 Feign 和 OpenFeign 的异同。

相同点

Feign 和 OpenFegin 具有以下相同点:

不同点

Feign 和 OpenFeign 具有以下不同:

OpenFeign 实现远程服务调用

下面我们就通过一个实例,来演示下通过 OpenFeign 是如何实现远程服务调用的。

1. 在 spring-cloud-demo2 下创建一个名为 micro-service-cloud-consumer-dept-feign 的 Spring Boot 模块,并在 pom.xml 中添加以下依赖。

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  3. xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
  4. <modelVersion>4.0.0</modelVersion>
  5. <parent>
  6. <artifactId>spring-cloud-demo2</artifactId>
  7. <groupId>net.biancheng.c</groupId>
  8. <version>0.0.1-SNAPSHOT</version>
  9. </parent>
  10.  
  11. <groupId>net.biancheng.c</groupId>
  12. <artifactId>micro-service-cloud-consumer-dept-feign</artifactId>
  13. <version>0.0.1-SNAPSHOT</version>
  14. <name>micro-service-cloud-consumer-dept-feign</name>
  15. <description>Demo project for Spring Boot</description>
  16. <properties>
  17. <java.version>1.8</java.version>
  18. </properties>
  19. <dependencies>
  20. <dependency>
  21. <groupId>net.biancheng.c</groupId>
  22. <artifactId>micro-service-cloud-api</artifactId>
  23. <version>${project.version}</version>
  24. </dependency>
  25. <dependency>
  26. <groupId>org.springframework.boot</groupId>
  27. <artifactId>spring-boot-starter-web</artifactId>
  28. </dependency>
  29. <dependency>
  30. <groupId>org.projectlombok</groupId>
  31. <artifactId>lombok</artifactId>
  32. <optional>true</optional>
  33. </dependency>
  34. <dependency>
  35. <groupId>org.springframework.boot</groupId>
  36. <artifactId>spring-boot-starter-test</artifactId>
  37. <scope>test</scope>
  38. </dependency>
  39. <!--Eureka Client 依赖-->
  40. <dependency>
  41. <groupId>org.springframework.cloud</groupId>
  42. <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
  43. </dependency>
  44. <!-- Ribbon 依赖-->
  45. <dependency>
  46. <groupId>org.springframework.cloud</groupId>
  47. <artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
  48. </dependency>
  49. <!--添加 OpenFeign 依赖-->
  50. <dependency>
  51. <groupId>org.springframework.cloud</groupId>
  52. <artifactId>spring-cloud-starter-openfeign</artifactId>
  53. </dependency>
  54. </dependencies>
  55.  
  56. <build>
  57. <plugins>
  58. <plugin>
  59. <groupId>org.springframework.boot</groupId>
  60. <artifactId>spring-boot-maven-plugin</artifactId>
  61. <configuration>
  62. <excludes>
  63. <exclude>
  64. <groupId>org.projectlombok</groupId>
  65. <artifactId>lombok</artifactId>
  66. </exclude>
  67. </excludes>
  68. </configuration>
  69. </plugin>
  70. </plugins>
  71. </build>
  72. </project>


2. 在 micro-service-cloud-consumer-dept-feign 下的类路径(即 /resources 目录)下,添加一个 application.yml,配置内容如下。

  1. server:
  2. port: 80
  3.  
  4. eureka:
  5. client:
  6. register-with-eureka: false #服务消费者可以不向服务注册中心注册服务
  7. service-url:
  8. defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/
  9. fetch-registry: true #服务消费者客户端需要去检索服务


3. 在 net.biancheng.c.service 包下创建一个名为 DeptFeignService 的接口,并在该接口上使用 @FeignClient 注解实现对服务接口的绑定,代码如下。

  1. package net.biancheng.c.service;
  2.  
  3. import net.biancheng.c.entity.Dept;
  4. import org.springframework.cloud.openfeign.FeignClient;
  5. import org.springframework.stereotype.Component;
  6. import org.springframework.web.bind.annotation.PathVariable;
  7. import org.springframework.web.bind.annotation.RequestMapping;
  8. import org.springframework.web.bind.annotation.RequestMethod;
  9.  
  10. import java.util.List;
  11.  
  12. //添加为容器内的一个组件
  13. @Component
  14. // 服务提供者提供的服务名称,即 application.name
  15. @FeignClient(value = "MICROSERVICECLOUDPROVIDERDEPT")
  16. public interface DeptFeignService {
  17. //对应服务提供者(8001、8002、8003)Controller 中定义的方法
  18. @RequestMapping(value = "/dept/get/{id}", method = RequestMethod.GET)
  19. public Dept get(@PathVariable("id") int id);
  20.  
  21. @RequestMapping(value = "/dept/list", method = RequestMethod.GET)
  22. public List<Dept> list();
  23. }


在编写服务绑定接口时,需要注意以下 2 点:


4. 在 net.biancheng.c.controller 包下,创建一个名为 DeptController_Consumer 的 Controller 类,代码如下。

  1. package net.biancheng.c.controller;
  2.  
  3. import net.biancheng.c.entity.Dept;
  4. import net.biancheng.c.service.DeptFeignService;
  5. import org.springframework.web.bind.annotation.PathVariable;
  6. import org.springframework.web.bind.annotation.RequestMapping;
  7. import org.springframework.web.bind.annotation.RestController;
  8.  
  9. import javax.annotation.Resource;
  10. import java.util.List;
  11.  
  12. @RestController
  13. public class DeptController_Consumer {
  14.  
  15. @Resource
  16. private DeptFeignService deptFeignService;
  17.  
  18. @RequestMapping(value = "/consumer/dept/get/{id}")
  19. public Dept get(@PathVariable("id") Integer id) {
  20. return deptFeignService.get(id);
  21. }
  22.  
  23. @RequestMapping(value = "/consumer/dept/list")
  24. public List<Dept> list() {
  25. return deptFeignService.list();
  26. }
  27. }


5. 在主启动类上添加 @EnableFeignClients 注解开启 OpenFeign 功能,代码如下。

  1. package net.biancheng.c;
  2.  
  3. import org.springframework.boot.SpringApplication;
  4. import org.springframework.boot.autoconfigure.SpringBootApplication;
  5. import org.springframework.cloud.openfeign.EnableFeignClients;
  6.  
  7. @SpringBootApplication
  8. @EnableFeignClients //开启 OpenFeign 功能
  9. public class MicroServiceCloudConsumerDeptFeignApplication {
  10.  
  11. public static void main(String[] args) {
  12. SpringApplication.run(MicroServiceCloudConsumerDeptFeignApplication.class, args);
  13. }
  14. }


Spring Cloud 应用在启动时,OpenFeign 会扫描标有 @FeignClient 注解的接口生成代理,并注人到 Spring 容器中。

6. 依次启动服务注册中心集群、服务提供者以及 micro-service-cloud-consumer-dept-feign,启动完成后,使用浏览器访问“http://eureka7001.com/consumer/dept/list”,结果如下图。

OpenFeign 实现服务调用
图1:OpenFeign 实现远程服务调用


7. 连续多次访问“http://eureka7001.com/consumer/dept/list”,结果如下图。

OpenFeign 默认负载均衡
图2:OpenFeign 负载均衡


从图 2 可以看出,由于 OpenFeign 集成了 Ribbon,因此它也实现了客户端的负载均衡,其默认负载均衡策略为轮询策略。

OpenFeign 超时控制

OpenFeign 客户端的默认超时时间为 1 秒钟,如果服务端处理请求的时间超过 1 秒就会报错。为了避免这样的情况,我们需要对 OpenFeign 客户端的超时时间进行控制。

下面我们就通过一个实例,来演示 OpenFeign 是如何进行超时控制的。

1. 在所有的服务提供者(服务端)的 DeptController 中添加一个响应时间为 5 秒的服务,代码如下。

  1. //超时测试,该服务的响应时间为 5 秒
  2. @RequestMapping(value = "/dept/feign/timeout")
  3. public String DeptFeignTimeout() {
  4. //暂停 5 秒
  5. try {
  6. TimeUnit.SECONDS.sleep(5);
  7. } catch (InterruptedException e) {
  8. e.printStackTrace();
  9. }
  10. return serverPort;
  11. }


2. 在 micro-service-cloud-consumer-dept-feign 的 DeptFeignService 接口中添加以下代码,绑定服务端刚刚添加的超时服务。

  1. @RequestMapping(value = "/dept/feign/timeout")
  2. public String DeptFeignTimeout();


3. 在 micro-service-cloud-consumer-dept-feign 的 DeptController_Consumer 添加以下代码。

  1. @RequestMapping(value = "/consumer/dept/feign/timeout")
  2. public String DeptFeignTimeout() {
  3. // openFeign-ribbon 客户端一般默认等待一秒钟,超过该时间就会报错
  4. return deptFeignService.DeptFeignTimeout();
  5. }


4. 重启所有服务提供者,使用浏览器依次访问“http://eureka7001.com:8001/dept/feign/timeout”、“http://eureka7001.com:8002/dept/feign/timeout”和“http://eureka7001.com:8003/dept/feign/timeout”,确保所有服务提供者提供的超时服务都能正常使用,如下图。

服务提供者超时服务
图3:服务提供者的超时服务


5. 重启 micro-service-cloud-consumer-dept-feign,使用浏览器访问“http://eureka7001.com/consumer/dept/feign/timeout”,结果如下图。

OpenFeign 超时报错
图4:OpenFeign 超时报错


6. 在 micro-service-cloud-consumer-dept-feign 的 application.yml 中添加以下配置,将超时时间设置为 6 秒。

  1. ribbon:
  2. ReadTimeout: 6000 #建立连接所用的时间,适用于网络状况正常的情况下,两端两端连接所用的时间
  3. ConnectionTimeout: 6000 #建立连接后,服务器读取到可用资源的时间

注:由于 OpenFeign 集成了 Ribbon ,其服务调用以及负载均衡在底层都是依靠 Ribbon 实现的,因此 OpenFeign 超时控制也是通过 Ribbon 来实现的。

7. 再次重启 micro-service-cloud-consumer-dept-feign,使用浏览器访问“http://eureka7001.com/consumer/dept/feign/timeout”,结果如下图。

OpenFeign 超时控制
图5:OpenFeign 超时控制

OpenFeign 日志增强

OpenFeign 提供了日志打印功能,我们可以通过配置调整日志级别,来了解请求的细节。

Feign 为每一个 FeignClient 都提供了一个 feign.Logger 实例,通过它可以对 OpenFeign 服务绑定接口的调用情况进行监控。

OpenFeign 日志打印功能的开启方式比较简单,下面我们就通过一个实例进行演示。

1. 在 micro-service-cloud-consumer-dept-feign 的 application.yml 中配置以下内容。

  1. logging:
  2. level:
  3. #feign 日志以什么样的级别监控该接口
  4. net.biancheng.c.service.DeptFeignService: debug


以上配置说明如下:


以上配置的含义就是,OpenFeign 以 debug 级别监控 net.biancheng.c.service.DeptFeignService 接口。

2. 在 net.biancheng.c. config 包下创建一个名为 ConfigBean 的配置类,代码如下。

  1. package net.biancheng.c.config;
  2.  
  3. import feign.Logger;
  4. import org.springframework.context.annotation.Bean;
  5. import org.springframework.context.annotation.Configuration;
  6.  
  7. @Configuration
  8. public class ConfigBean {
  9. /**
  10. * OpenFeign 日志增强
  11. * 配置 OpenFeign 记录哪些内容
  12. */
  13. @Bean
  14. Logger.Level feginLoggerLevel() {
  15. return Logger.Level.FULL;
  16. }
  17. }


该配置的作用是通过配置的 Logger.Level 对象告诉 OpenFeign 记录哪些日志内容。

 Logger.Level 的具体级别如下:


3. 重启 micro-service-cloud-consumer-dept-feign,使用浏览器访问“http://eureka7001.com/consumer/dept/list”,控制台输出如下。

 

标签:Feign,OpenFeign,Spring,dept,cloud,注解,Cloud
来源: https://www.cnblogs.com/55zjc/p/16537136.html