构建微服务架构(OpenFeign篇)
该篇文档,前置环境条件为:构建微服务架构(eureka篇)
该篇文档,前置代码下载:下载
该篇文档,全部完成后的代码下载:下载
原文链接:https://blog.csdn.net/u011863024/article/details/114298270
GitHub地址:https://github.com/spring-cloud/spring-cloud-openfeign
SpringCloud地址:https://cloud.spring.io/spring-cloud-static/Hoxton.SR1/reference/htmlsingle/#spring-cloud-openfeign
OpenFeign 是什么
Feign 是一个声明式 WebService 客户端。使用 Feign 能让编写 Web Service 客户端更加简单。它的使用方法是定义一个服务接口然后在上面添加注解。Feign也支持可拔插式的编码器和解码器。Spring Cloud 对 Feign 进行了封装,使其支持了 Spring MVC 标准注解和 HttpMessageConverters。Feign 可以与 Eureka 和 Ribbon 组合使用以支持负载均衡。
Feign 能干什么
Feign 旨在使编写 Java Http 客户端变得更容易。
前面在使用 Ribbon+RestTemplate 时,利用 RestTemplate 对 http 请求的封装处理,形成了一套模版化的调用方法。但是在实际开发中,由于对服务依赖的调用可能不止一处,往往一个接口会被多处调用,所以通常都会针对每个微服务自行封装一些客户端类来包装这些依赖服务的调用。所以,Feign 在此基础上做了进一步封装,由他来帮助我们定义和实现依赖服务接口的定义。在 Feign 的实现下,我们只需创建一个接口并使用注解的方式来配置它(以前是 Dao 接口上面标注 Mapper 注解,现在是一个微服务接口上面标注一个 Feign 注解即可),即可完成对服务提供方的接口绑定,简化了使用 Spring cloud Ribbon 时,自动封装服务调用客户端的开发量。
Feign 集成了 Ribbon
利用 Ribbon 维护了 Payment 的服务列表信息,并且通过轮询实现了客户端的负载均衡。而与 Ribbon 不同的是,通过 feign 只需要定义服务绑定接口且以声明式的方法,优雅而简单的实现了服务调用。
Feign 和 OpenFeign 两者区别
Feign 是 Spring Cloud 组件中的一个轻量级 RESTful 的 HTTP 服务客户端 Feign 内置了 Ribbon,用来做客户端负载均衡,去调用服务注册中心的服务。Feign 的使用方式是:使用 Feign 的注解定义接口,调用这个接口,就可以调用服务注册中心的服务。
1 2 3 4
| <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-feign</artifactId> </dependency>
|
OpenFeign 是 Spring Cloud 在 Feign 的基础上支持了 SpringMVC 的注解,如 @RequesMapping 等等。OpenFeign 的 @Feignclient 可以解析 SpringMVc 的 @RequestMapping 注解下的接口,并通过动态代理的方式产生实现类,实现类中做负载均衡并调用其他服务。
1 2 3 4
| <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency>
|
OpenFeign 服务调用
接口+注解:微服务调用接口 + @FeignClient
1. 新建 cloud-consumer-fenign-order80 项目
2. cloud-consumer-fenign-order80 项目 pom.xml 文件如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56
| <?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>springcloud2021</artifactId> <groupId>com.sevattal.springcloud</groupId> <version>1.0-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>cloud-consumer-fenign-order80</artifactId> <dependencies> <!--openfeign--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency> <!--eureka client--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency> <!-- 引入自己定义的api通用包,可以使用Payment支付Entity --> <dependency> <groupId>com.sevattal.springcloud</groupId> <artifactId>cloud-api-commons</artifactId> <version>${project.version}</version> </dependency> <!--web--> <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>
|
2. cloud-consumer-fenign-order80 项目 application.yml 文件如下
1 2 3 4 5 6 7
| server: port: 80 eureka: client: register-with-eureka: false service-url: defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/
|
3. cloud-consumer-fenign-order80 项目 主启动类
1 2 3 4 5 6 7 8 9 10 11
| package com.sevattal.springcloud; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.openfeign.EnableFeignClients; @SpringBootApplication @EnableFeignClients public class OrderFeignMain80 { public static void main(String[] args) { SpringApplication.run(OrderFeignMain80.class, args); } }
|
4. cloud-consumer-fenign-order80 项目 业务类创建
业务逻辑接口 + @FeignClient 配置调用 provider 服务
新建 PaymentFeignService 接口并新增注解 @FeignClient
1 2 3 4 5 6 7 8 9 10 11 12 13
| package com.sevattal.springcloud.service; import com.sevattal.springcloud.entities.CommonResult; import com.sevattal.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-PROVIDER-SERVICE") public interface PaymentFeignService { @GetMapping(value = "/payment/get/{id}") public CommonResult<Payment> getPaymentById(@PathVariable("id") Long id); }
|
5. cloud-consumer-fenign-order80 项目 控制层 Controller
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| package com.sevattal.springcloud.controller; import com.sevattal.springcloud.entities.CommonResult; import com.sevattal.springcloud.entities.Payment; import com.sevattal.springcloud.service.PaymentFeignService; import lombok.extern.slf4j.Slf4j; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RestController; import javax.annotation.Resource; @RestController @Slf4j public class OrderFeignController{ @Resource private PaymentFeignService paymentFeignService; @GetMapping(value = "/consumer/payment/get/{id}") public CommonResult<Payment> getPaymentById(@PathVariable("id") Long id) { return paymentFeignService.getPaymentById(id); } }
|
6. 测试
先启动 2 个 eureka 集群 7001/7002
再启动 2 个微服务 8001/8002
启动 OpenFeign 启动
1
| http://localhost/consumer/payment/get/1
|
Feign 自带负载均衡配置项
OpenFeign 超时控制
超时设置,故意设置超时演示出错情况
1.服务提供方 8001/8002 故意写暂停程序
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| @RestController @Slf4j public class PaymentController { ... @Value("${server.port}") private String serverPort; ... @GetMapping(value = "/payment/feign/timeout") public String paymentFeignTimeout() { // 业务逻辑处理正确,但是需要耗费3秒钟 try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); } return serverPort; } ... }
|
2.服务消费方 80 添加超时方法 PaymentFeignService
1 2 3 4 5 6 7
| @Component @FeignClient(value = "CLOUD-PROVIDER-SERVICE") public interface PaymentFeignService{ ... @GetMapping(value = "/payment/feign/timeout") public String paymentFeignTimeout(); }
|
3.服务消费方 80 添加超时方法 OrderFeignController
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| @RestController @Slf4j public class OrderFeignController { @Resource private PaymentFeignService paymentFeignService; ... @GetMapping(value = "/consumer/payment/feign/timeout") public String paymentFeignTimeout() { // OpenFeign客户端一般默认等待1秒钟 return paymentFeignService.paymentFeignTimeout(); } }
|
4.测试:
多次刷新http://localhost/consumer/payment/feign/timeout
将会跳出错误Spring Boot默认错误页面,主要异常:feign.RetryableException:Read timed out executing GET http://CLOUD-PROVIDER-SERVICE/payment/feign/timeout。
OpenFeign 默认等待 1 秒钟,超过后报错
YML 文件里需要开启 OpenFeign 客户端超时控制
# 设置 feign 客户端超时时间(OpenFeign 默认支持 ribbon )(单位:毫秒)
ribbon:
# 指的是建立连接所用的时间,适用于网络状况正常的情况下,两端连接所用的时间
ReadTimeout: 5000
# 指的是建立连接后从服务器读取到可用资源所用的时间
ConnectTimeout: 5000
OpenFeign 日志增强
日志打印功能
Feign 提供了日志打印功能,我们可以通过配置来调整日恙级别,从而了解 Feign 中 Http 请求的细节。
说白了就是对 Feign 接口的调用情况进行监控和输出
日志级别
NONE:默认的,不显示任何日志;
BASIC:仅记录请求方法、URL、响应状态码及执行时间;
HEADERS:除了 BASIC 中定义的信息之外,还有请求和响应的头信息;
FULL:除了 HEADERS 中定义的信息之外,还有请求和响应的正文及元数据。
1. 配置日志 bean
1 2 3 4 5 6 7 8 9 10 11 12
| import feign.Logger; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class FeignConfig { @Bean Logger.Level feignLoggerLevel() { return Logger.Level.FULL; } }
|
2. YML 文件里需要开启日志的 Feign 客户端
1 2 3 4
| logging: level: # feign日志以什么级别监控哪个接口 com.sevattal.springcloud.service.PaymentFeignService: debug
|
后台日志查看
得到更多日志信息。