OpenFeign概述

Feign是一个声明式WebService客户端,使用Feign能让编写WebService客户端更加简单。

它的使用方式是定义一个服务接口然后在上面添加注解。Feign也支持可拔插式的编码器和解码器。Spring Cloud对Feign进行了封装,使其支持了Spring MVC标准注解和HttpMessageConverters。Feign可以与Eureka和Ribbon组合使用以支持负载均衡。

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

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

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

Feign和OpenFeign对比:

Feign OpenFeign
Feign是Spring Cloud组件中的一个轻量级RestFul的Http服务客户端。Feign内置了Ribbon,用来做客户端负载均衡,去调用服务注册中心的服务。Feign的使用方式是:使用Feign的注解定义接口,调用这个接口就可以调用服务注册中心的服务。 OpenFeign是Spring Cloud在Fiegn的基础上支持了SpringMVC的注解,如@RequestMapping等。OpenFeign的@FeignClient可以解析SpringMVC的@RequestMapping注解下的接口,并通过动态代理的方式产生实现类,实现类中做负载均衡并调用其他服务。
GroupID:org.springframework.cloud
ArtifactID:spring-cloud-starter-feign
GroupID:org.springframework.cloud
ArtifactID:spring-cloud-starter-openfeign

环境搭建

  1. 创建Maven工程

  2. 引入pom依赖

    1. <dependencies>
    2. <!-- 引入OpenFeign依赖 -->
    3. <dependency>
    4. <groupId>org.springframework.cloud</groupId>
    5. <artifactId>spring-cloud-starter-openfeign</artifactId>
    6. </dependency>
    7. <dependency>
    8. <groupId>org.springframework.cloud</groupId>
    9. <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
    10. </dependency>
    11. <dependency>
    12. <groupId>com.study</groupId>
    13. <artifactId>cloud-api-commons</artifactId>
    14. <version>1.0-SNAPSHOT</version>
    15. </dependency>
    16. <dependency>
    17. <groupId>org.springframework.boot</groupId>
    18. <artifactId>spring-boot-starter-web</artifactId>
    19. </dependency>
    20. <dependency>
    21. <groupId>org.springframework.boot</groupId>
    22. <artifactId>spring-boot-actuator</artifactId>
    23. </dependency>
    24. <dependency>
    25. <groupId>org.springframework.boot</groupId>
    26. <artifactId>spring-boot-devtools</artifactId>
    27. </dependency>
    28. <dependency>
    29. <groupId>org.projectlombok</groupId>
    30. <artifactId>lombok</artifactId>
    31. </dependency>
    32. <dependency>
    33. <groupId>org.springframework.boot</groupId>
    34. <artifactId>spring-boot-starter-test</artifactId>
    35. </dependency>
    36. </dependencies>
  1. 创建主启动类,在主启动类上使用 @EnableFeignClients注解
    @SpringBootApplication
    @EnableFeignClients
    public class OrderFeignMain80 {
     public static void main(String[] args) {
         SpringApplication.run(OrderFeignMain80.class, args);
     }
    }
    
  1. 添加主配置文件 ```yaml server: port: 80

eureka: client:

# 只从eureka上获取服务,不把自己注册到eureka上
register-with-eureka: false
service-url: http://localhost:7001/eureka/,http://cold-thunder:7002/eureka


5. 
编写FeignClient接口,并在接口中加入需要调用的方法
```java
@Component  // 加入ioc容器
@FeignClient("CLOUD-PAYMENT-SERVICE")  // 使用FeignClient声明要调用的服务在eureka上注册的服务名
public interface PaymentFeignService {

    // 要调用的payment服务的controller中的方法
    @GetMapping("/payment/get/{id}")
    CommonResult getPaymentById(@PathVariable("id") Long id);

    @PostMapping("/payment/create")
    CommonResult create(@RequestBody Payment payment);
}
  1. 编写controller进行测试

    @RestController
    @Slf4j
    public class OrderFeignController {
    
     @Autowired
     private PaymentFeignService paymentFeignService;
    
     @GetMapping("/consumer/getPaymentFeign/{id}")
     public CommonResult<Payment> getPaymentFeign(@PathVariable("id") Long id) {
         CommonResult paymentById = paymentFeignService.getPaymentById(id);
         return paymentById;
     }
    }
    

超时控制

OpenFeign默认等待1秒钟,超过后报错。

演示程序:

生产者中加入一个Controller,在Controller加入延时:

@GetMapping("/payment/feignTimeout")
public String paymentFeignTimeout() {
    try {
        TimeUnit.SECONDS.sleep(4);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    return serverPort;
}

消费者中使用FeignClient调用生产者的该Controller:

@GetMapping("/consumer/feignTimeout")
public String getPaymentFeignTimeout() {
    return paymentFeignService.paymentFeignTimeout();
}

此时在浏览器中:

如果直接访问生产者的Controller,浏览器在4秒后可以正常响应。

如果通过消费者进行访问,则浏览器在1秒后报错。

默认Feign客户端只等待1秒钟,如果服务端的处理超过一秒钟,Feign就不再等待直接返回报错。

为了避免服务端业务处理超过1秒钟的情况,需要设置Feign客户端的超时控制。

方案1:feign是通过Ribbon实现的超时控制,可以直接配置Ribbon的超时时间:

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

方案2:通过FeignClientProperties的config属性进行配置:

Map config;key为FeignClient的name(即要调用的服务名,也可以配置成default),value为配置项

feign:
  client:
    config:
      default:  # FeignClient的name属性(即要调用的服务名),也可以配置为default
        readTimeout: 5000
        connectTimeout: 5000

日志打印功能

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

日志级别:

  • NONE

    默认的,不显示任何日志

  • BASIC

    仅记录请求方法、URL、响应状态码及执行时间

  • HEADERS

    除了BASIC中定义的信息之外,还有请求和响应的头信息

  • FULL

    除了HEADERS中定义的信息之外,还有请求和响应的正文及元数据

配置日志:

feign:
  client:
    config:
      default:
        loggerLevel: FULL

logging:
  level:
      # 日志监控的类、级别
    com.study.springcloud.service.PaymentFeignService: debug