微服务间的远程接口调用: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两个依赖parentgroupIdio.github.iweidujiang/groupIdartifactId07-open-feign/artifactIdversion1.0.0/version/parentartifactIdopen-feign-service/artifactIddependenciesdependencygroupIdorg.springframework.cloud/groupIdartifactIdspring-cloud-starter-loadbalancer/artifactId/dependencydependencygroupIdorg.springframework.cloud/groupIdartifactIdspring-cloud-starter-openfeign/artifactId/dependency/dependencies07-open-feign/pom.xml子模块定义modulesmodulefeign-provider/modulemoduleopen-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 苏渡苇 */SpringBootApplicationEnableDiscoveryClientEnableFeignClientspublicclassOpenFeignServiceApplication{/** * 应用入口。 * * param args 启动参数 */publicstaticvoidmain(String[]args){SpringApplication.run(OpenFeignServiceApplication.class,args);}}2.创建一个 Feign 客户端接口添加FeignClient注解无需再加Servicepackageio.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(namenacos-provider)publicinterfaceProductService{/** * 调用远程服务 nacos-provider 的 /product/{id} 接口。 * * param id 商品 ID * return 商品信息 */GetMapping(/product/{id})StringgetProductById(PathVariable(id)Longid);}关于FeignClient注解需要知道name 是一个任意的客户端名称用于创 Spring Cloud LoadBalancer 客户端urlurl一般用于调试可以手动指定FeignClient调用的地址configurationFeigin 配置类可自定义 Feign 的 EncodeDecodeLogLevelContractfallback定义容错的类当远程调用的接口失败或者超时的时候会调用对应接口的容错逻辑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.productServiceproductService;}/** * 通过 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{privatestaticfinalMapLong,StringPRODUCT_MAPnewHashMap();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(valuedelay,defaultValue0)intdelaySeconds){if(delaySeconds0){try{TimeUnit.SECONDS.sleep(delaySeconds);}catch(InterruptedExceptione){Thread.currentThread().interrupt();}}returnserverPortPRODUCT_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 LevelFeignConfig配置类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依赖dependencygroupIdorg.springframework.cloud/groupIdartifactIdspring-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可通过请求参数delay3模拟慢调用超时curlhttp://localhost:6061/product/1?delay3或在feign-provider中直接访问curlhttp://localhost:8080/product/1?delay3调用效果本系列文章代码仓库https://github.com/iweidujiang/spring-cloud-alibaba-lab以上就是本文的全部内容了本次导航结束。先赞后看养成习惯。举手之劳赞有余香。本文创作于 2022-08-18 。代码仓库已更新https://github.com/iweidujiang/spring-cloud-alibaba-lab