✨ OpenFeign服务接口调用
- OpenFeign & Feign
- Feign基本介绍
- OpenFeign基本介绍
- 二者对比
- OpenFeign的使用
- 新建Module:cloud-consumer-feign-order80
- pom依赖
- application.yml全局配置文件
- 主启动类
- Service
- 1. 业务逻辑接口+@FeignClient配置调用provider服务
- 2. 新建PaymentOpenFeignService接口并新增注解@FeignClient
- Controller
- 测试
- OpenFeign的超时控制
- 演示超时出错的情况
- YML中需要开启OpenFeign客户端超时控制
- yml
- 超时控制详细介绍
- 代码 & 测试
- OpenFeign日志打印功能
- 基本介绍
- 日志级别
- application.yml全局配置文件
- 配置类
- 测试
📃个人主页:不断前进的皮卡丘
🌞博客描述:梦想也许遥不可及,但重要的是追梦的过程,用博客记录自己的成长,记录自己一步一步向上攀登的印记
🔥个人专栏:微服务专栏
OpenFeign & Feign
Feign基本介绍
- Netflix Feign 是 Netflix 公司发布的一种 实现负载均衡和服务调用的开源组件。Spring Cloud 将其与 Netflix 中的其他开源服务组件(例如** Eureka、Ribbon 以及 Hystrix** 等)一起整合进 Spring Cloud Netflix 模块中,整合后全称为 Spring Cloud Netflix Feign。
- Feign 对 Ribbon 进行了集成,利用 Ribbon 维护了一份可用的服务列表信息,并且通过轮询实现了客户端的负载均衡。而与Ribbon不同的是,通过Feign只需要定义服务绑定接口且以声明式的方法,优雅而简单的实现了服务调用。
- 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 进行远程服务调用时,常用注解如下表。
二者对比
- Feign是Spring Cloud组件中的一个轻量级RESTful的HTTP服务客户端,Feign内置了Ribbon用来做客户端负载均衡,去调用服务注册中心的服务。Feign的使用方式:使用Feign的注解定义接口,调用这个接口就可用调用服务注册中心的服务了。
- OpenFeign是Spring Cloud在Feign的基础上支持了Spring MVC注解,比如@RequestMapping等,OpenFeign的@FeignClient可用解析Spring MVC的@RequestMapping注解下面的接口,并且通过动态代理的方式产生实现类,实现类中做负载均衡并且调用其他服务。
OpenFeign的使用
新建Module:cloud-consumer-feign-order80
pom依赖
注意:OpenFeign是自带Ribbon的
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>cloud2022</artifactId>
<groupId>com.zyh.springcloud</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>cloud-consumer-feign-order80</artifactId>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<!--添加的依赖 -->
<dependencies>
<!--openfeign-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<!-- 这个模块是之前抽取公共代码的时候,提取出来的模块 -->
<dependency>
<groupId>com.zyh.springcloud</groupId>
<artifactId>cloud-api-commons</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>
application.yml全局配置文件
server:
port: 80
spring:
application:
name: cloud-consumer-feign-order80
eureka:
client:
register-with-eureka: true
fetch-registry: true
service-url:
defaultZone: http://localhost:7001/eureka
主启动类
Service
1. 业务逻辑接口+@FeignClient配置调用provider服务
2. 新建PaymentOpenFeignService接口并新增注解@FeignClient
package com.zyh.springcloud.service;
import com.zyh.springcloud.entities.CommonResult;
import com.zyh.springcloud.entities.Payment;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
/**
* 远程调用接口
*
* @author zengyihong
* @create 2022--10--14 13:46
*/
//声明bean对象,创建代理对象,进行远程调用
@Service
//指定调用微服务名称
@FeignClient(value = "CLOUD-PAYMENT-SERVICE")
public interface PaymentOpenFeignService {
@GetMapping(value = "/payment/get/{id}")
/**
* 接口方法声明要求:需要和被调用的服务的controller方法声明保持一致
*/
CommonResult getPaymentById(@PathVariable("id") Long id);
@PostMapping(value = "/payment/create")
CommonResult<Payment> create(@RequestBody Payment payment);
}
Controller
package com.zyh.springcloud.controller;
import com.zyh.springcloud.entities.CommonResult;
import com.zyh.springcloud.entities.Payment;
import com.zyh.springcloud.service.PaymentOpenFeignService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
/**
* @author zengyihong
* @create 2022--10--14 13:49
*/
@RestController
public class OrderOpenFeignController {
@Autowired
private PaymentOpenFeignService paymentOpenFeignService;
@GetMapping(value = "/consumer/payment/get/{id}")
public CommonResult<Payment> getPaymentById(@PathVariable("id") Long id) {
return paymentOpenFeignService.getPaymentById(id);
}
}
测试
- 先启动Eureka7001
- 再启动3个微服务8001/8002/8003
- 启动OpenFeign微服务:cloud-consumer-feign-order80
- http://localhost:7001/
- http://localhost:80/consumer/payment/get/1
- Feign自带负载均衡配置项(默认使用轮询)
OpenFeign的超时控制
演示超时出错的情况
OpenFeign 客户端的默认超时时间为 1 秒钟,如果服务端处理请求的时间超过 1 秒就会报错。为了避免这样的情况,有时候我们需要设置Feign客户端的超时控制,也就是Ribbon的超时时间,因为Feign集成了Ribbon进行负载均衡。
开启payment8001,还有cloud-consumer-feign-order80,重新进行测试
YML中需要开启OpenFeign客户端超时控制
yml
#设置Feign客户端超时时间(openfeign默认支持ribbon)
ribbon:
ReadTimeout: 3000
ConnectTimeout: 3000
MaxAutoRetries: 1 #同一台实例最大重试次数,不包括首次调用
MaxAutoRetriesNextServer: 1 #重试负载均衡其他的实例最大重试次数,不包括首次调用
OkToRetryOnAllOperations: false #是否所有操作都重试
#注意:
#默认情况下,GET 请求方式不管是连接异常还是读取异常,都会进行重试
#非GET请求方式,只有在连接异常的时候, 才会进行重试
#hystrix的超时时间
hystrix:
command:
default:
execution:
timeout:
enabled: true
isolation:
thread:
timeoutInMilliseconds: 9000
超时控制详细介绍
一般情况下都是 ribbon 的超时时间(<)hystrix的超时时间(因为涉及到ribbon的重试机制)
因为ribbon的重试机制和Feign的重试机制有冲突,所以源码中默认关闭Feign的重试机制,源码如下
要开启Feign的重试机制如下:(Feign默认重试五次 源码中有)
@Bean
Retryer feignRetryer() {
return new Retryer.Default();
}
根据上面的参数计算重试的次数:MaxAutoRetries+MaxAutoRetriesNextServer+(MaxAutoRetries *MaxAutoRetriesNextServer) 即重试3次 则一共产生4次调用
如果在重试期间,时间超过了hystrix的超时时间,便会立即执行熔断,fallback。所以要根据上面配置的参数计算hystrix的超时时间,使得在重试期间不能达到hystrix的超时时间,不然重试机制就会没有意义
hystrix超时时间的计算: (1 + MaxAutoRetries + MaxAutoRetriesNextServer) * ReadTimeout 即按照以上的配置 hystrix的超时时间应该配置为 (1+1+1)*3=9秒
当ribbon超时后且hystrix没有超时,便会采取重试机制。当OkToRetryOnAllOperations设置为false时,只会对get请求进行重试。如果设置为true,便会对所有的请求进行重试,如果是put或post等写操作,如果服务器接口没做幂等性,会产生不好的结果,所以OkToRetryOnAllOperations慎用。
如果不配置ribbon的重试次数,默认会重试一次
注意:
默认情况下,GET方式请求无论是连接异常还是读取异常,都会进行重试
非GET方式请求,只有连接异常时,才会进行重试
代码 & 测试
然后我们需要把payment8001/8002/8003这三个模块都启动起来
OpenFeign日志打印功能
基本介绍
- OpenFeign 提供了日志打印功能,我们可以通过配置调整日志级别,来了解Feign中Http请求的细节。
- Feign 为每一个 FeignClient 都提供了一个 feign.Logger 实例,通过它可以对 OpenFeign 服务绑定接口的调用情况进行监控。
日志级别
- NONE:默认的,不显示任何日志
- BASIC:仅记录请求方法、RUL、响应状态码及执行时间
- HEADERS:除了BASIC中定义的信息之外,还有请求和响应的头信息
- FULL:除了HEADERS中定义的信息之外,还有请求和响应的正文及元数据
application.yml全局配置文件
logging:
level:
com.zyh.springcloud.service.PaymentOpenFeignService: debug
com.zyh.springcloud.service.PaymentOpenFeignService是开启 @FeignClient 注解的接口(即服务绑定接口)的完整类名。也可以只配置部分路径,表示监控该路径下的所有服务绑定接口