image.png

一、OpenFeign概述

1、OpenFeign是什么

官网 源码
Feign是一个声明式WebService客户端,使用Feign能让编写Web Service客户端更加简单,只需要创建一个接口并添加注解即可
他的使用方法是定义一个服务接口然后在上面添加注解,Feign也支持可插拔式的编码器和解码器。Spring Cloud 对Feign进行了封装。使其支持了SpringMVC 标准注解和HttpMessageConverters。Feign可以与Eureka和Ribbon组合使用以支持负载均衡。

小结:

  • Feign是一个声明式Rest/WebService客户端,使用Feign能让WebService客户端更加简单。
  • 只需创建一个接口并在接口上添加注解即可。

    2、Feign作用

    Feign旨在使编写Java Http客户端变得更容易。

    声明式远程方法调用

前面在使用Ribbon+RestTemplate时,利用RestTemplate对http请求的封装处理,形成了一套模版化的调用方法。但是在实际开发中,由于对服务依赖的调用可能不止一处,往往一个接口会被多处调用,所以通常都会针对每个微服务自行封装一些客户端类来包装这些依赖服务的调用。所以,Feign在此基础上做了进一步封装,由他来帮助我们定义和实现依赖服务接口的定义。在Feign的实现下,我们只需创建一个接口并使用注解的方式来配置它(以前是Dao接口上面标注Mapper注解,现在是一个微服务接口上面标注一个Feign注解即可),即可完成对服务提供方的接口绑定,简化了使用Spring cloud Ribbon时,自动封装服务调用客户端的开发量。

即服务提供方有哪些接口,Feign里面即有与之对应的方法直接调用。

Feign集成了Ribbon
利用Ribbon维护了Payment的服务列表信息,并且通过轮询实现了客户端的负载均衡。而与Ribbon不同的是,通过feign只需要以声明式的方法定义服务且绑定接口,优雅而简单的实现了服务调用

3、Feign与OpenFeign的区别

image.png

二、OpenFeign使用步骤

以前我们在消费者服务调用生产者服务时,采用Ribbon+restTemplate进行客户端服务调用和负载均衡。现在采用OpenFeign绑定服务接口。

Feign 是使用在消费端!

接口+注解 ——— 微服务调用接口+@FeignClient 注解

1、创建cloud-consumer-openfeign-order80 模块

2、写pom

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <project xmlns="http://maven.apache.org/POM/4.0.0"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  5. <parent>
  6. <artifactId>jdk8cloud2021</artifactId>
  7. <groupId>com.atguigu.springcloud</groupId>
  8. <version>1.0-SNAPSHOT</version>
  9. </parent>
  10. <modelVersion>4.0.0</modelVersion>
  11. <artifactId>cloud-consumer-openfeign-order80</artifactId>
  12. <properties>
  13. <maven.compiler.source>8</maven.compiler.source>
  14. <maven.compiler.target>8</maven.compiler.target>
  15. </properties>
  16. <dependencies>
  17. <!--openfeign 新增-->
  18. <dependency>
  19. <groupId>org.springframework.cloud</groupId>
  20. <artifactId>spring-cloud-starter-openfeign</artifactId>
  21. </dependency>
  22. <!--eureka client-->
  23. <dependency>
  24. <groupId>org.springframework.cloud</groupId>
  25. <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
  26. </dependency>
  27. <!-- 引入自己定义的api通用包,可以使用Payment支付Entity -->
  28. <dependency>
  29. <groupId>com.atguigu.springcloud</groupId>
  30. <artifactId>cloud-api-commons</artifactId>
  31. <version>${project.version}</version>
  32. </dependency>
  33. <!--web-->
  34. <dependency>
  35. <groupId>org.springframework.boot</groupId>
  36. <artifactId>spring-boot-starter-web</artifactId>
  37. </dependency>
  38. <dependency>
  39. <groupId>org.springframework.boot</groupId>
  40. <artifactId>spring-boot-starter-actuator</artifactId>
  41. </dependency>
  42. <!--一般基础通用配置-->
  43. <dependency>
  44. <groupId>org.springframework.boot</groupId>
  45. <artifactId>spring-boot-devtools</artifactId>
  46. <scope>runtime</scope>
  47. <optional>true</optional>
  48. </dependency>
  49. <dependency>
  50. <groupId>org.projectlombok</groupId>
  51. <artifactId>lombok</artifactId>
  52. <optional>true</optional>
  53. </dependency>
  54. <dependency>
  55. <groupId>org.springframework.boot</groupId>
  56. <artifactId>spring-boot-starter-test</artifactId>
  57. <scope>test</scope>
  58. </dependency>
  59. </dependencies>
  60. </project>

image.png
OpenFeign整合了Ribbon,所以具有负载均衡的功能

3、写yaml

不将其注册到Eureka作为微服务,而是作为一个Feign客户端

  1. server:
  2. port: 80
  3. eureka:
  4. client:
  5. # 表示不将其注入Eureka作为微服务,不作为Eureak客户端了,而是作为Feign客户端
  6. register-with-eureka: false
  7. service-url:
  8. # 集群版
  9. defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka,http://eureka7003.com:7003/eureka

4、主启动类

主启动类上添加 @EableFeignClients注解

  1. @SpringBootApplication
  2. @EnableFeignClients //不作为Eureak客户端了,而是作为Feign客户端
  3. public class OrderOpenFeignMain80 {
  4. public static void main(String[] args) {
  5. SpringApplication.run(OrderOpenFeignMain80.class, args);
  6. }
  7. }

5、业务类

声明一个远程调用服务接口,不需要能被Springboot扫描到

5.1 业务逻辑接口+@FeignClient配置调用provider服务

新建PaymentFeignService接口并新增注解@FeignClient
@FeignClient("provider微服务名字")
注意:

  • 这里声明的方法签名,必须和provider微服务(服务提供者微服务)中的controller中方法的签名一致
  • 如果需要传递参数,那么@RequestParam@RequestBody @PathVariable 不能省 必加 ```java package com.atguigu.springcloud.service;

import com.atguigu.springcloud.entities.CommonResult; import com.atguigu.springcloud.entities.Payment; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.stereotype.Component; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable;

@Component @FeignClient(value = “CLOUD-PAYMENT-SERVICE”) //作为一个Feign功能绑定的的接口 public interface PaymentFeignService { @GetMapping(value = “/payment/get/{id}”) public CommonResult getPaymentById(@PathVariable(“id”) Long id); }

  1. 这样就可以找到CLOUD-PAYMENT-SERVICE微服务下面的/payment/get/{id}这个地址。<br />这也就说明:
  2. - PaymentFeignService接口+@FeignClient注解,完成Feign的包装调用
  3. - 指明找哪个微服务上面的地址
  4. 主启动类开启Feign(增加**@EnableFeignClients**注解),接口上使用**@FeignClient**,组合使用<br />![image.png](https://cdn.nlark.com/yuque/0/2021/png/22423156/1632234249641-1ef57686-a790-4667-8998-aa4f11837d6f.png#clientId=ufd2bd2be-a9b6-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=163&id=u228a639d&margin=%5Bobject%20Object%5D&name=image.png&originHeight=163&originWidth=664&originalType=binary&ratio=1&rotation=0&showTitle=false&size=21634&status=done&style=none&taskId=ufc558689-701b-4245-b8b0-60bd6c817eb&title=&width=664)
  5. <a name="IOkwL"></a>
  6. ### 5.2 consumer的控制层Controller
  7. 通过自己的80 Service接口层,去调用服务提供者中的接口
  8. ```java
  9. @RestController
  10. @Slf4j
  11. public class OrderFeignController {
  12. //直接将PaymentFeignService的对象注入
  13. @Resource
  14. private PaymentFeignService paymentFeignService;
  15. @GetMapping("/consumer/payment/get/{id}")
  16. public CommonResult<Payment> getPaymentById(@PathVariable("id") Long id) {
  17. return paymentFeignService.getPaymentById(id);
  18. }
  19. }

5.3 测试

先启动两个Eureka集群 7001/7002
再启动两个paymentprovider微服务 8001/8002
启动使用OpenFeign的OrderFeign80
测试成功:
消费者微服务没有注册在Eureka中;通过消费者本身的接口地址,去调用生产者微服务对应的接口,并且可以实现负载均衡
image.pngimage.png
比较符合我们的编程习惯,在80中还是controller调用service,service再去调用8001的controller。
image.png

三、OpenFeign超时控制

1、超时设置,故意设置超时演示出错情况

1.1 服务提供方8001故意写暂停程序

  1. @GetMapping("/payment/feign/timeout")
  2. public String paymentFeignTimeout() {
  3. try {
  4. TimeUnit.SECONDS.sleep(3);
  5. } catch (InterruptedException e) {
  6. e.printStackTrace();
  7. }
  8. return serverPort;
  9. }

注意:假如你8002也启动的话 里面没有写paymenttimeout方法,会报404错误;我这里在8002也写了该方法。

1.2 服务消费方80在PaymentFeignService添加超时方法

  1. @Component
  2. @FeignClient(value = "CLOUD-PAYMENT-SERVICE") //作为一个Feign功能绑定的的接口
  3. public interface PaymentFeignService {
  4. @GetMapping(value = "/payment/get/{id}")
  5. public CommonResult<Payment> getPaymentById(@PathVariable("id") Long id);
  6. @GetMapping("/payment/feign/timeout")
  7. public String paymentFeignTimeout();
  8. }

1.3 消费方80添加超时方法到controller

  1. @GetMapping("/consumer/payment/feign/timeout")
  2. public String paymentFeignTimeout() {
  3. //openfeign-ribbon,客户端一般默认等待1s
  4. return paymentFeignService.paymentFeignTimeout();
  5. }

1.4 测试

8001服务方自测:没有问题(3s后出结果)
image.png
80消费方测试:超时报错 OpenFeign默认等待时间为1s,超过后报错
image.png

2、超时报错

2.1 OpenFeign默认等待时间为1秒钟,超过后报错

默认Feign客户端只等待一秒钟,但是服务段处理需要超过1秒钟,导致Feign客户端不想等待了,直接返回报错。
为了避免这种请况,有时候我们需要设置Feign客户端的超时控制。

Feign 默认是支持Ribbon ,Feign依赖里自己带了RibbonFeign客户端的负载均衡和超时控制都由Ribbon控制

2.2 配置超时时间

为了避免上述情况,我们需要设置Feign客户端的超时等待时间。
yml文件中开启配置:
注意:我配置ReadTimeout/ConnectTimeout时yml没有提示。

  1. #设置feign客户端超时时间(OpenFeign默认支持ribbon)
  2. ribbon:
  3. #指的是建立连接后从服务器读取到可用资源所用的时间
  4. ReadTimeout: 5000
  5. #指的是建立连接所用的时间,适用于网络状况正常的情况下,两端连接所用的时间
  6. ConnectTimeout: 5000

3.0.3版本
feign.client.config.default.connect-timeout=5000 feign.client.config.default.read-timeout=5000

2.3 测试

image.png

四、OpenFeign日志打印功能

Feign提供了日志打印功能,我们可以通过配置来调整日志级别,从而了解Feign中Http请求的细节。
说白了就是:对Feign接口的调用情况进行监控和输出。

1、日志级别

NONE 默认的,不显示任何日志
BASIC 仅记录请求方法、URL、响应状态码及执行时间
HEADERS 除了BASIC中定义的信息之外,还有请求和响应的头信息
FULL 除了HEADERS中定义的信息外,还有请求和响应的正文及元数据。

2、配置日志

配置在消费端

2.1 配置日志bean

  1. package com.atguigu.springcloud.config;
  2. import feign.Logger;
  3. import org.springframework.context.annotation.Bean;
  4. import org.springframework.context.annotation.Configuration;
  5. @Configuration
  6. public class FeignConfig {
  7. @Bean
  8. Logger.Level feignLoggerLevel()
  9. {
  10. return Logger.Level.FULL;
  11. }
  12. }

2.2 配置消费端的yml文件

  1. logging:
  2. level:
  3. # feign 日志以什么级别监控哪个接口
  4. com.atguigu.springcloud.service.PaymentFeignService: debug

2.3 查看后台日志

image.png