微服务间的远程接口调用:OpenFeign 的使用
前言:OpenFeign 能做什么?
OpenFeign是一种声明式、模板化的 HTTP 客户端。在 Spring Cloud 中使用 OpenFeign ,可以做到使用 HTTP 请求访问远程服务,就像调用本地方法一样的,开发者完全感知不到这是在调用远程方法,更感知不到在访问 HTTP 请求。
其用法就是编写一个接口,在接口上添加注解。如此就能轻而易举的调用远程服务。
有如此强大的东西,我们肯定不能放过使用的机会,就像有时你有特殊的要求必须拉别的女孩的手,而此时有个中间人能帮你实现这个愿望,你拉别的女孩子的手就像拉自己女朋友的手一样方便!
OpenFeign在微服务中的作用就像中间方一样,当你需要调用另一个微服务的接口时,使用OpenFeign就像调用本服务的接口一样丝滑。
操练:欲善其事,先利其器
本章代码仓库:https://github.com/iweidujiang/spring-cloud-alibaba-lab
示例模块:
07-open-feign,包含feign-provider与open-feign-service。
既然是远程调用,那肯定至少得有 2 个微服务。本章在07-open-feign下新建open-feign-service子模块,调用同目录feign-provider(注册到 Nacos 的服务名为nacos-provider)。
open-feign-service引入spring-cloud-starter-loadbalancer和spring-cloud-starter-openfeign两个依赖:
<parent><groupId>io.github.iweidujiang</groupId><artifactId>07-open-feign</artifactId><version>1.0.0</version></parent><artifactId>open-feign-service</artifactId><dependencies><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-loadbalancer</artifactId></dependency><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-openfeign</artifactId></dependency></dependencies>07-open-feign/pom.xml子模块定义:
<modules><module>feign-provider</module><module>open-feign-service</module></modules>如何在open-feign-service服务中调用nacos-provider服务的接口呢?前面第 2 章nacos-consumer使用了LoadBalancer和RestTemplate进行调用,现在我们在open-feign-service使用OpenFeign来进行调用。
创建 FeignClient 接口
要将 Feign 引入到到项目中:
1.首先需要在启动类上添加@EnableFeignClients注解:
packageio.github.iweidujiang.lab07.consumer;importorg.springframework.boot.SpringApplication;importorg.springframework.boot.autoconfigure.SpringBootApplication;importorg.springframework.cloud.client.discovery.EnableDiscoveryClient;importorg.springframework.cloud.openfeign.EnableFeignClients;/** * OpenFeign 服务消费者启动类。 * * @author 苏渡苇 */@SpringBootApplication@EnableDiscoveryClient@EnableFeignClientspublicclassOpenFeignServiceApplication{/** * 应用入口。 * * @param args 启动参数 */publicstaticvoidmain(String[]args){SpringApplication.run(OpenFeignServiceApplication.class,args);}}2.创建一个 Feign 客户端接口,添加@FeignClient注解(无需再加@Service):
packageio.github.iweidujiang.lab07.consumer.client;importorg.springframework.cloud.openfeign.FeignClient;importorg.springframework.web.bind.annotation.GetMapping;importorg.springframework.web.bind.annotation.PathVariable;/** * 远程调用 nacos-provider 服务的 Feign 客户端。 * * @author 苏渡苇 */@FeignClient(name="nacos-provider")publicinterfaceProductService{/** * 调用远程服务 nacos-provider 的 /product/{id} 接口。 * * @param id 商品 ID * @return 商品信息 */@GetMapping("/product/{id}")StringgetProductById(@PathVariable("id")Longid);}关于FeignClient注解,需要知道:
name: 是一个任意的客户端名称,用于创 Spring Cloud LoadBalancer 客户端;url:url一般用于调试,可以手动指定@FeignClient调用的地址;configuration:Feigin 配置类,可自定义 Feign 的 Encode,Decode,LogLevel,Contract;fallback:定义容错的类,当远程调用的接口失败或者超时的时候,会调用对应接口的容错逻辑,fallback 执行的类必须实现@FeignClient标记的接口;fallbackFactory:工厂类,用于生成 fallback 类实例,通过此属性可以实现每个接口通用的容错逻辑,以达到减少重复的代码;path:定义当前 FeignClient 的统一前缀。
本案例只是用name属性指定调用的服务名称,容错属性后续可与 Sentinel 整合再说。
3.控制层通过FeignClient远程调用
packageio.github.iweidujiang.lab07.consumer.controller;importio.github.iweidujiang.lab07.consumer.client.ProductService;importorg.springframework.web.bind.annotation.GetMapping;importorg.springframework.web.bind.annotation.PathVariable;importorg.springframework.web.bind.annotation.RestController;/** * OpenFeign 调用测试接口。 * * @author 苏渡苇 */@RestControllerpublicclassProductController{privatefinalProductServiceproductService;publicProductController(ProductServiceproductService){this.productService=productService;}/** * 通过 OpenFeign 远程查询商品。 * * @param id 商品 ID * @return 远程调用结果 */@GetMapping("/product/{id}")publicStringgetProduct(@PathVariable("id")Longid){returnproductService.getProductById(id);}}控制层引入被@FeignClient标记的接口ProductService,直接调用getProductById方法即可远程调用nacos-provider的/product/{id}。远程服务feign-provider的处理逻辑如下:
packageio.github.iweidujiang.lab07.provider.controller;importorg.springframework.beans.factory.annotation.Value;importorg.springframework.web.bind.annotation.GetMapping;importorg.springframework.web.bind.annotation.PathVariable;importorg.springframework.web.bind.annotation.RequestParam;importorg.springframework.web.bind.annotation.RestController;importjava.util.HashMap;importjava.util.Map;importjava.util.concurrent.TimeUnit;/** * 商品查询接口,供 OpenFeign 远程调用。 * * @author 苏渡苇 */@RestControllerpublicclassProductController{privatestaticfinalMap<Long,String>PRODUCT_MAP=newHashMap<>();static{PRODUCT_MAP.put(1L,"香飘飘奶茶");PRODUCT_MAP.put(2L,"雀巢咖啡");PRODUCT_MAP.put(3L,"百事可乐");}@Value("${server.port}")privateStringserverPort;/** * 根据 ID 查询商品。 * * @param id 商品 ID * @param delaySeconds 模拟慢调用延迟秒数 * @return 商品信息 */@GetMapping("/product/{id}")publicStringgetProduct(@PathVariableLongid,@RequestParam(value="delay",defaultValue="0")intdelaySeconds){if(delaySeconds>0){try{TimeUnit.SECONDS.sleep(delaySeconds);}catch(InterruptedExceptione){Thread.currentThread().interrupt();}}returnserverPort+":"+PRODUCT_MAP.getOrDefault(id,"未知商品");}}验证
直接访问本服务:http://localhost:6061/product/3 ,可以看到调用了远程服务nacos-provider的接口:
从结果看,还实现了访问服务的负载均衡!
优化:事无巨细,极致体验
日志
OpenFeign提供了日志打印功能,我们可以通过配置来调整日志级别,从而了解OpenFeign中 Http 请求的细节。
通过设置日志,可以对 Feign 接口的调用情况进行监控和输出。
OpenFeign的日志级别主要有以下几种:
NONE:默认的,不显示任何日志;BASIC:仅记录请求方法、URL、响应状态码及执行时间;HEADERS:除了 BASIC 中定义的信息之外,还有请求和响应的头信息;FULL:除了 HEADERS 中定义的信息之外,还有请求和响应的正文及元数据。
使用步骤:
1.设置 Feign Logger Level(FeignConfig配置类):
packageio.github.iweidujiang.lab07.consumer.config;importfeign.Logger;importorg.springframework.context.annotation.Bean;importorg.springframework.context.annotation.Configuration;/** * OpenFeign 全局配置。 * * @author 苏渡苇 */@ConfigurationpublicclassFeignConfig{/** * 开启 Feign 详细日志。 * * @return Feign 日志级别 */@BeanLogger.LevelfeignLoggerLevel(){returnLogger.Level.FULL;}}2.在配置文件中给指定的 FeignClient 接口加指定的日志级别
logging:level:io.github.iweidujiang.lab07.consumer.client.ProductService:debug使用效果:
请求的详细情况就以日志的形式打印出来了。
关于超时时间
spring-cloud-starter-openfeign支持spring-cloud-starter-loadbalancer。我们在项目中已经添加了spring-cloud-starter-loadbalancer依赖:
<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-loadbalancer</artifactId></dependency>这样在@FeignClient注解中,当设定了name = "nacos-provider"客户端名称后,便默认使用了Spring Cloud LoadBalancer进行负载均衡访问nacos-provider,在老版本中,集成的是Ribbon,它默认的响应时间是 1 s,可以通过ribbon.ReadTimeout和ribbon.ConnectTimeout来设置客户端超时时间。
Spring Cloud Loadbalancer 默认没有超时时间的限制。
但是我们依然可以在默认客户端(default)和命名客户端上(注解 FeignClient 设置的 name,比如本demo中的 nacos-provider)配置超时。
OpenFeign 使用两个超时参数:
connectTimeout防止由于服务器处理时间长而阻塞调用者。readTimeout从连接建立时开始,在返回响应时间过长时触发。
具体设置方式:
feign:client:config:# 默认的超时时间设置default:connectTimeout:5000readTimeout:5000# 在指定的 FeignClient 设置超时时间,覆盖默认的设置nacos-provider:connectTimeout:1000readTimeout:1000loggerLevel:full假如设置nacos-provider的超时时间为 1s,可通过请求参数delay=3模拟慢调用超时:
curl"http://localhost:6061/product/1?delay=3"或在feign-provider中直接访问:
curl"http://localhost:8080/product/1?delay=3"调用效果:
本系列文章代码仓库:https://github.com/iweidujiang/spring-cloud-alibaba-lab
以上就是本文的全部内容了,本次导航结束。
先赞后看,养成习惯。
举手之劳,赞有余香。
本文创作于 2022-08-18 。
代码仓库已更新:https://github.com/iweidujiang/spring-cloud-alibaba-lab
