目录
OpenFeign是一种声明式、模板化的HTTP客户端。在Spring Cloud中使用OpenFeign,可以做到使用HTTP请求访问远程服务,就像调用本地方法一样的,开发者完全感知不到这是在调用远程方法,更感知不到在访问HTTP请求,用法其实就是编写一个接口,在接口上添加注解即可。
可以简单理解它是借鉴Ribbon的基础之上,封装的一套服务接口+注解的方式的远程调用器。
它的宗旨是在编写Java Http客户端接口的时候变得更加容易,其底层整合了Ribbon,所以也支持负载均衡。
之前我们使用Ribbon的时候,利用RestTemplate对Http请求进行封装处理,但是在实际开发中,由于对服务依赖的调用不可能就一处,往往一个接口会被多处调用,所以通常都会针对每个微服务自行封装一些客户端类来包装这些依赖服务的调用。所以OpenFeign在此基础之上做了进一步的封装,由它来帮助我们定义和实现依赖服务接口的定义,我们只需创建一个接口并使用注解的方式来配置它,即可完成对微服务提供方的接口绑定,简化Ribbon的操作。
首先我们要明确使用OpenFeign是使用在消费者端去远程调用,就必须要是用FeignClient注解,来标注要调用的服务提供者名称,然后在通过一个接口来定义要调用的方法,所以我们首先新建一个Model:cloudalibaba-openfeign-consumer-8888
需要在父级项目引入对应依赖
- <dependency>
- <groupId>org.springframework.cloudgroupId>
- <artifactId>spring-cloud-starter-openfeignartifactId>
- <version>${openfeign-version}version>
- dependency>
pom
- "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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
- <parent>
- <artifactId>cloud2021artifactId>
- <groupId>com.lwz.springcloudgroupId>
- <version>1.0-SNAPSHOTversion>
- parent>
- <modelVersion>4.0.0modelVersion>
- <artifactId>cloudalibaba-openfeign-consumer-8888artifactId>
- <dependencies>
- <dependency>
- <groupId>org.springframework.bootgroupId>
- <artifactId>spring-boot-starter-webartifactId>
- dependency>
- <dependency>
- <groupId>org.springframework.bootgroupId>
- <artifactId>spring-boot-starter-actuatorartifactId>
- dependency>
- <dependency>
- <groupId>org.springframework.bootgroupId>
- <artifactId>spring-boot-devtoolsartifactId>
- <scope>runtimescope>
- <optional>trueoptional>
- dependency>
- <dependency>
- <groupId>org.projectlombokgroupId>
- <artifactId>lombokartifactId>
- <optional>trueoptional>
- dependency>
- <dependency>
- <groupId>org.springframework.bootgroupId>
- <artifactId>spring-boot-starter-testartifactId>
- <scope>testscope>
- dependency>
- <dependency>
- <groupId>com.alibaba.cloudgroupId>
- <artifactId>spring-cloud-starter-alibaba-nacos-discoveryartifactId>
- dependency>
- <dependency>
- <groupId>com.lwz.springcloudgroupId>
- <artifactId>cloud-api-commonsartifactId>
- <version>${project.version}version>
- dependency>
- <dependency>
- <groupId>org.springframework.cloudgroupId>
- <artifactId>spring-cloud-starter-openfeignartifactId>
- dependency>
- <dependency>
- <groupId>org.springframework.bootgroupId>
- <artifactId>spring-bootartifactId>
- dependency>
- dependencies>
-
- <build>
- <plugins>
- <plugin>
- <groupId>org.springframework.bootgroupId>
- <artifactId>spring-boot-maven-pluginartifactId>
- plugin>
- plugins>
- build>
-
- project>
yml
- server:
- port: 8888
- spring:
- application:
- name: nacos-consumer-openFeign
- cloud:
- nacos:
- discovery:
- server-addr: localhost:8848
主启动
- @SpringBootApplication
- @EnableDiscoveryClient
- @EnableFeignClients//添加此注解
- public class OpenfeignConsumer8888Application {
-
- public static void main(String[] args) {
- SpringApplication.run(OpenfeignConsumer8888Application.class, args);
- }
-
- }
调用服务提供者对外提供接口
这里要调用的是服务提供者9003/9004
- package com.lwz.cloud.service;
-
- import com.lwz.springcloud.entities.JsonResult;
- 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;
-
- /**
- * 此接口就是配合使用OpenFeign的接口,
- * 在此接口中添加@FeignClient接口同时标注
- * 要调用的服务端名称,同时使用与服务提供者
- * 方法签名一致的抽象方法来表示远程调用的
- * 具体内容
- */
- @Service
- //表示远程调用服务名称
- @FeignClient("nacos-provider")
- public interface OpenFeignService {
- /**
- * 此方法表示远程调用info/{id}接口
- */
- @GetMapping("info/{id}")
- public JsonResult
getSql(@PathVariable("id") Long id); - }
控制器
- package com.lwz.cloud.controller;
-
- import com.lwz.cloud.service.OpenFeignService;
- import com.lwz.springcloud.entities.JsonResult;
- 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;
-
- @RestController
- public class OpenFeignController {
-
- @Autowired
- private OpenFeignService openFeignService;
-
- @GetMapping("getInfo/{id}")
- public JsonResult
getInfo(@PathVariable("id") Long id){ - return openFeignService.getSql(id);
- }
-
- }
测试
启动9003/9004/8888,能够远程调用的同时还有负载均衡效果
http://localhost:8888/getInfo/2
OpenFeign 客户端默认等待1秒钟,但是如果服务端业务超过1秒,则会报错。为了避免这样的情况,我们需要设置feign客户端的超时控制。
解决办法
由于OpenFeign 底层是ribbon 。所以超时控制由ribbon来控制。在yml文件中配置
首先演示超时效果,我们现在9003/9004上设置一个延迟3秒执行的方法,来模仿长业务线调用。
- @GetMapping("/timeOut")
- public String timeOut() {
- try {
- System.out.println("延迟响应");
- TimeUnit.SECONDS.sleep(3);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- return serverPort;
- }
客户端8888通过OpenFeign来进行调用
- //OpenFeginController
- @GetMapping("/testTimeout")
- public String TestTimeout(){
- return openFeignService.timeOut();
- }
-
- //OpenFeignService
- @GetMapping("/timeOut")
- public String timeOut();
测试
http://localhost:8888/testTimeout
- 2023-06-09 22:58:31.625 INFO 8000 --- [erListUpdater-0] c.netflix.config.ChainedDynamicProperty : Flipping property: nacos-provider.ribbon.ActiveConnectionsLimit to use NEXT property: niws.loadbalancer.availabilityFilteringRule.activeConnectionsLimit = 2147483647
- 2023-06-09 22:58:32.903 ERROR 8000 --- [nio-8888-exec-1] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is feign.RetryableException: Read timed out executing GET http://nacos-provider/timeOut] with root cause
-
- java.net.SocketTimeoutException: Read timed out
- at java.net.SocketInputStream.socketRead0(Native Method) ~[na:1.8.0_202]
首先我们需要在8888消费者端的yml文件中配置超时时间,因为OpenFeign本身整合了Ribbon所以,这里其实我们用的是Ribbon来配置
- server:
- port: 8888
- spring:
- application:
- name: nacos-consumer-openfegin
- cloud:
- nacos:
- discovery:
- server-addr: localhost:8848
-
- #设置feign客户端超时时间(OpenFeign默认支持ribbon)
- ribbon:
- #指的是建立连接后从服务器读取到可用资源所用的时间
- ReadTimeout: 5000
- #指的是建立连接所用的时间,适用于网络状况正常的情况下,两端连接所用的时间
- ConnectTimeout: 5000
测试
重启8888,起不来的话,把注释或空行部分删除
Feign 提供了日志打印功能,我们可以通过配置来调整日志级别,从而了解 Feign 中 Http 请求的细节。
简单理解,就是对Feign接口的调用情况进行监控和输出
日志级别:
具体使用
1、需要在启动类中通过@Bean注解注入OpenFeign的日志功能
- @SpringBootApplication
- @EnableFeignClients
- public class OpenFeginConsumer8888Application {
-
- public static void main(String[] args) {
- SpringApplication.run(OpenFeginConsumer8888Application.class, args);
- }
-
- @Bean
- Logger.Level feignLoggerLevel(){
- //开启详细日志
- return Logger.Level.FULL;
- }
- }
2、在yml中配置中配置
- server:
- port: 8888
- spring:
- application:
- name: nacos-consumer-openfegin
- cloud:
- nacos:
- discovery:
- server-addr: localhost:8848
-
- #设置feign客户端超时时间(OpenFeign默认支持ribbon)
- ribbon:
- #指的是建立连接所用的时间,适用于网络状况正常的情况下,两端连接所用的时间
- ReadTimeout: 5000
- #指的是建立连接后从服务器读取到可用资源所用的时间
- ConnectTimeout: 5000
-
- logging:
- level:
- # Openfeign日志以什么级别监控哪个接口
- com.lwz.cloud.service.OpenFeignService: debug
测试
重启8888,启动成功把控制台日志清空,访问 http://localhost:8888/testTimeout
- 2023-06-09 23:17:21.193 DEBUG 8152 --- [nio-8888-exec-2] com.lwz.cloud.service.OpenFeignService : [OpenFeignService#timeOut] ---> GET http://nacos-provider/timeOut HTTP/1.1
- 2023-06-09 23:17:21.193 DEBUG 8152 --- [nio-8888-exec-2] com.lwz.cloud.service.OpenFeignService : [OpenFeignService#timeOut] ---> END HTTP (0-byte body)
- 2023-06-09 23:17:24.200 DEBUG 8152 --- [nio-8888-exec-2] com.lwz.cloud.service.OpenFeignService : [OpenFeignService#timeOut] <--- HTTP/1.1 200 (3006ms)
- 2023-06-09 23:17:24.200 DEBUG 8152 --- [nio-8888-exec-2] com.lwz.cloud.service.OpenFeignService : [OpenFeignService#timeOut] connection: keep-alive
- 2023-06-09 23:17:24.201 DEBUG 8152 --- [nio-8888-exec-2] com.lwz.cloud.service.OpenFeignService : [OpenFeignService#timeOut] content-length: 4
- 2023-06-09 23:17:24.201 DEBUG 8152 --- [nio-8888-exec-2] com.lwz.cloud.service.OpenFeignService : [OpenFeignService#timeOut] content-type: text/plain;charset=UTF-8
- 2023-06-09 23:17:24.201 DEBUG 8152 --- [nio-8888-exec-2] com.lwz.cloud.service.OpenFeignService : [OpenFeignService#timeOut] date: Fri, 09 Jun 2023 15:17:24 GMT
- 2023-06-09 23:17:24.201 DEBUG 8152 --- [nio-8888-exec-2] com.lwz.cloud.service.OpenFeignService : [OpenFeignService#timeOut] keep-alive: timeout=60
- 2023-06-09 23:17:24.202 DEBUG 8152 --- [nio-8888-exec-2] com.lwz.cloud.service.OpenFeignService : [OpenFeignService#timeOut]
- 2023-06-09 23:17:24.202 DEBUG 8152 --- [nio-8888-exec-2] com.lwz.cloud.service.OpenFeignService : [OpenFeignService#timeOut] 9003
- 2023-06-09 23:17:24.202 DEBUG 8152 --- [nio-8888-exec-2] com.lwz.cloud.service.OpenFeignService : [OpenFeignService#timeOut] <--- END HTTP (4-byte body)
引入OpenFegin
在之前的8084项目中引入对应的依赖
- <dependency>
- <groupId>org.springframework.cloudgroupId>
- <artifactId>spring-cloud-starter-openfeignartifactId>
- dependency>
激活Sentinel对OpenFeign的支持,所以配置yml
- # 激活Sentinel对OpenFeign的支持
- feign:
- sentinel:
- enabled: true
主启动类要添加@EnableFeignClients注解
OpenFegin接口编写
- //当没有成功调用/info/{id}接口时会走fallback属性标注的类型的处理方法
- @Service
- @FeignClient(value = "nacos-provider",fallback = FeignServiceImpl.class)
- public interface FeignService {
- /**
- * 远程调用对应方法
- */
- @GetMapping("info/{id}")
- public JsonResult
getSql(@PathVariable("id") Long id); - }
实现类必须添加@Component注解,否则无法注入到容器中
- @Component
- public class FeignServiceImpl implements FeignService {
-
- @Override
- public JsonResult
getSql(Long id) { - return new JsonResult<>(444,"服务降级返回!");
- }
- }
控制器
- //DemoController
- @Autowired
- private FeignService feignService;
-
- @GetMapping("getInfo/{id}")
- public JsonResult
getInfo(@PathVariable("id") Long id){ - if(id > 3){
- throw new RuntimeException("没有该id");
- }
- return feignService.getSql(id);
- }
测试
此时如果我们访问http://localhost:8084/getInfo/2的地址,是没有问题的,但是如果此时我们人为结束9003/9004服务,这个时候就会触发fallback属性对应的处理类型,完成服务降级。
http://localhost:8084/getInfo/2
断开9003&9004服务以后
Spring Cloud Alibaba - Sentinel(一)
Spring Cloud Alibaba - Sentinel(二)
Spring Cloud Alibaba - Sentinel源码分析(一)
懂与不懂都是收获!