OpenFeign服务接口调用
一、前世今生
Netflix Feign 是 Netflix 公司发布的一种实现负载均衡和服务调用的开源组件。Spring Cloud 将其与 Netflix 中的其他开源服务组件(例如 Eureka、Ribbon 以及 Hystrix 等)一起整合进 Spring Cloud Netflix 模块中,整合后全称为 Spring Cloud Netflix Feign。 Feign 对 Ribbon 进行了集成,利用 Ribbon 维护了一份可用服务清单,并通过 Ribbon 实现了客户端的负载均衡。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 等。
springcloud官方文档参考:https://cloud.spring.io/spring-cloud-static/Hoxton.SR1/reference/htmlsingle/#spring-cloud-openfeign
GitHub参考文档:https://github.com/spring-cloud/spring-cloud-openfeign
OpenFeign 常用注解
使用 OpenFegin 进行远程服务调用时,常用注解如下表。
| 注解 | 说明 |
|---|---|
| @FeignClient | 该注解用于通知 OpenFeign 组件对 @RequestMapping 注解下的接口进行解析,并通过动态代理的方式产生实现类,实现负载均衡和服务调用。 |
| @EnableFeignClients | 该注解用于开启 OpenFeign 功能,当 Spring Cloud 应用启动时,OpenFeign 会扫描标有 @FeignClient 注解的接口,生成代理并注册到 Spring 容器中。 |
| @RequestMapping | Spring MVC 注解,在 Spring MVC 中使用该注解映射请求,通过它来指定控制器(Controller)可以处理哪些 URL 请求,相当于 Servlet 中 web.xml 的配置。 |
| @GetMapping | Spring MVC 注解,用来映射 GET 请求,它是一个组合注解,相当于 @RequestMapping(method = RequestMethod.GET) 。 |
| @PostMapping | Spring MVC 注解,用来映射 POST 请求,它是一个组合注解,相当于 @RequestMapping(method = RequestMethod.POST) 。 |
Spring Cloud Finchley 及以上版本一般使用 OpenFeign 作为其服务调用组件。由于 OpenFeign 是在 2019 年 Feign 停更进入维护后推出的,因此大多数 2019 年及以后的新项目使用的都是 OpenFeign,而 2018 年以前的项目一般使用 Feign。但是目前OpenFeign也进入了维护阶段
三、Feign和OpenFeign 对比
下面我们就来对比下 Feign 和 OpenFeign 的异同。
相同点
Feign 和 OpenFegin 具有以下相同点:
- Feign 和 OpenFeign 都是 Spring Cloud 下的远程调用和负载均衡组件。
- Feign 和 OpenFeign 作用一样,都可以实现服务的远程调用和负载均衡。
- Feign 和 OpenFeign 都对 Ribbon 进行了集成,都利用 Ribbon 维护了可用服务清单,并通过 Ribbon 实现了客户端的负载均衡。
- Feign 和 OpenFeign 都是在服务消费者(客户端)定义服务绑定接口并通过注解的方式进行配置,以实现远程服务的调用。
不同点
Feign 和 OpenFeign 具有以下不同:
- Feign 和 OpenFeign 的依赖项不同,Feign 的依赖为 spring-cloud-starter-feign,而 OpenFeign 的依赖为 spring-cloud-starter-openfeign。
- Feign 和 OpenFeign 支持的注解不同,Feign 支持 Feign 注解和 JAX-RS 注解,但不支持 Spring MVC 注解;OpenFeign 除了支持 Feign 注解和 JAX-RS 注解外,还支持 Spring MVC 注解。
四、案例演示
4.1.新建cloud-consumer-feign-order80
创建maven项目

设置模块名称

4.2.设置POM
在pom中引入依赖:
<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-server</artifactId> </dependency> <dependency> <groupId>com.augus.springcloud</groupId> <artifactId>cloud-api-common</artifactId> <version>1.0-SNAPSHOT</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>
4.2.设置配置文件
创建application.yml配置文件,内容如下:
server:
port: 80
eureka:
client:
register-with-eureka: false
fetch-registry: true
service-url:
# 将自己注册到两个eureka集群中
defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka
4.3.设置主启动类
主要是需要添加上@EnableFeignClients 这个注解,表示使用OpenFeign
package com.augus.cloud; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.netflix.eureka.EnableEurekaClient; import org.springframework.cloud.openfeign.EnableFeignClients; @SpringBootApplication @EnableEurekaClient @EnableFeignClients public class OrderFeignMain80 { public static void main(String[] args) { SpringApplication.run(OrderFeignMain80.class,args); } }
4.4.业务类
业务逻辑接口+@FeignClient配置调用provider服务
创建service包,在下面创建 PaymentFigenService 接口,内容如下:
package com.augus.cloud.service; import com.augus.cloud.pojo.CommonResult; import com.augus.cloud.pojo.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; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; @Component @FeignClient(value = "CLOUD-PAYMENT-SERVICE") //指定调用的服务 public interface PaymentFigenService { //指定调用的服务的那个接口:直接复制服务提供者的接口即可 @GetMapping(value = "/payment/get/{id}") CommonResult<Payment> getPaymentById(@PathVariable("id") Long id); //指定调用的服务的那个接口 @PostMapping(value = "/payment/create") CommonResult<Payment> create(@RequestBody Payment payment); }
注意:上述接口需要通过 @FeignClient(value = "CLOUD-PAYMENT-SERVICE") 指定调用的是那个服务,只写服务名,不需要http://,切记切记这里一定注意,同时这里的接口实际上指定的就是去调用服务提供者的那个接口,所以将服务提供者的controller中的接口直接复制进来即可
4.5.创建controller
在下面创建 OrderFeigenController 内容如下:
package com.augus.cloud.controller; import com.augus.cloud.pojo.CommonResult; import com.augus.cloud.pojo.Payment; import com.augus.cloud.service.PaymentFigenService; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; @RestController @Slf4j public class OrderFeigenController { @Autowired private PaymentFigenService paymentFigenService; //指定调用的服务接口 @GetMapping(value = "/consumer/payment/get/{id}") public CommonResult<Payment> create(@PathVariable("id") Long id){ return paymentFigenService.getPaymentById(id); } //指定调用的服务接口 @PostMapping(value = "/consumer/payment/create") CommonResult<Payment> create(@RequestBody Payment payment){ return paymentFigenService.create(payment); } }
4.6.测试
访问:http://localhost/consumer/payment/get/1,会发现8001和8002轮询出现,因为OpenFeign里面包含的ribbon,所以直接就有服务间的负载均衡

五、OpenFeign超时控制
OpenFeign在进行调用服务提供者的时候,他的默认等待时间,只有1s,如果超过1s所调用的服务还没有返回信息,就会报错,下面呢,我们将通过案例来演示,解决超时控制
5.1.在服务提供方8001、8002controller中添加方法进行模拟
在8001和8002controller中添加的内容如下(后续直接复制即可):
@GetMapping("/payment/feign/timeout")
public String paymentFeignTimeout() throws InterruptedException {
//设置暂停3s,模拟超时
TimeUnit.SECONDS.sleep(3);
return serverPort;
}
5.2.在服务消费方cloud-consumer-feign-order80添加超时方法
在 PaymentFeignService下添加接口,内容如下:
@GetMapping("/payment/feign/timeout")
String paymentFeignTimeout();
如图所示:

5.3.在cloud-consumer-feign-order80的controller中添加内容:
在 OrderFeigenController 中添加方法:
@GetMapping("/consumer/payment/feign/timeout")
public String paymentFeignTimeout(){
//OpenFeign调用默认只有1s的等待时间,如果超时还获取不到结果会报错
return paymentFigenService.paymentFeignTimeout();
}
5.4.测试
浏览器访问:http://localhost/consumer/payment/feign/timeout 这里会报超时错误原因在于 OpenFeign默认等待1秒钟,超过后报错

5.5.设置超时控制
上面的问题,在我们后续的开发中,有的服务由于本身的原因调用时间会超过1s,这时候报错肯定不行,就需要进行设置,避免出现错误,那么设置如下,只需要在cloud-consumer-feign-order80的application.yml中加入如下内容:
# 设置feign客户端超时时间(OpenFeign默认支持ribbon) ribbon: # 指的是建立连接所用的时间,适用于网络状态正常的情况下,两端连接所用的时间 ReadTimeout: 5000 # 指的是建立连接后从服务器读取到可用资源所用的时间 ConnectTimeout: 5000
注意:上面设置的内容是对于Ribbon的设置,因为在OpenFeign中引入后,里面就包含了ribbon,所以虽然说的是设置的OpenFeign超时控制,但是实际上设置的是Ribbon,依赖关系如下图:

设置后重启 cloud-consumer-feign-order80 服务,然后访问:http://localhost/consumer/payment/feign/timeout,发现不会在报错,会出现如下内容:

六、OpenFeign日志打印功能
6.1.OpenFeign日志打印功能
在通过OpenFeign调用服务进行联调的时候,有时候需要详细的了解OpenFeign的调用,如头、状态码、时间、接口等等;OpenFeign就自身就提供了一个详细的日志打印功能,可以通过配置来调整日志级别,从而了解Feign中Http请求的细节,也就是对Feign接口的调用情况进行监控和输出。
6.2.日志级别
在OpenFeign中日志分为如下级别:
| 级别 | 描述 |
| NONE | 默认的,不显示任何日志; |
| BASIC | 仅记录请求方法、URL、响应状态码及执行时间; |
| HEADERS | 除了BASIC中定义的信息之外,还有请求和响应的头信息; |
| FULL | 除了HEADERS中定义的信息之外,还有请求和响应的正文及元数据。 |
6.3.配置日志bean
在com.augus.cloud包下创建子包config,在里面创建 FeignConfig.java 内容如下:
package com.augus.cloud.config; import feign.Logger; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class FeignConfig { /** * feignClient配置日志级别 * * @return */ @Bean public Logger.Level feignLoggerLevel() { // 设置日志的级别为:FULL (请求和响应的头信息,请求和响应的正文及元数据) return Logger.Level.FULL; } }
6.4.在cloud-consumer-feign-order80的配置文件中开启日志配置
在application.yml中添加如下内容:
logging:
level:
# 设置以什么级别监视那个接口
com.augus.cloud.service.PaymentFeignService: debug
6.5.测试
重启 cloud-consumer-feign-order80 访问:http://localhost/consumer/payment/get/1 在控制台即可看到打印出的信息:


浙公网安备 33010602011771号