今天通过这篇文章,结合之前的知识,我们一起来了解一下 Spring Cloud 技术体系中另一个最核心的组件之一 Fegin。
Spring Cloud Feign 是一套基于 Netflix Feign 实现的 HTTP 客户端工具,主要作用是简化 HTTP 客户端的开发和维护工作。
传统的模式下,当我们要对某个接口发起 HTTP 请求时,首先会封装 HTTP 请求报文,然后发起请求,最后处理响应结果。例如之前介绍过的RestTemplate
工具。
其实这三步骤,可以编写一个动态代理类来帮助我们以一种更简洁、易于维护的方式完成 HTTP 请求的调用。Fegin 的实现逻辑大体就是这种思路,我们只需要创建一个接口并添加@FeignClient
注解,然后配置相关的请求方法既可完成 HTTP 请求工作,剩下的就交给代理类来完成。不过底层,使用的依然是Apache HttpClient
或OkHttp
发送请求。
与原生 Feign 组件相比,Spring Cloud Feign 还扩展了对 Spring MVC 注解的支持,同时还整合了 Ribbon 提供客户端的负载均衡实现,以及 Hystrix 服务熔断器。
下面我们通过具体的例子,看看如何使用 Feign 来实现 HTTP 请求。
与之前介绍 Ribbon 类似,依次创建eureka-server
、eureka-provider-1
、eureka-provider-2
工程,就不重复粘贴了。
根据eureka-consumer
复制一个服务消费者工程,命名为eureka-consumer-feign
,并在pom.xml
中引入 Feign 依赖包,示例如下:
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.5.4.RELEASE</version> <relativePath/> </parent> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-eureka</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-feign</artifactId> </dependency> </dependencies> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>Edgware.SR3</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement>
然后,创建一个服务启动类并添加@EnableFeignClients
注解,表示开启扫描 Spring Cloud Feign 客户端。
@EnableFeignClients @EnableDiscoveryClient @SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class,args); } }
接着,创建一个接口并使用@FeignClient
注解指定要调用的目标服务实例名称,接口中定义的各个方法使用 Spring MVC 的注解就可以指定要调用的目标服务接口地址,示例如下:
/** * 配置要调用的服务实例名称 */ @FeignClient(name = "eureka-provider") public interface RpcService { /** * 要调用的目标服务接口地址 * @return */ @RequestMapping(value = "/hello") String hello(); }
最后,创建一个controller
,通过定义的 feign 客户端来调用服务提供方的接口。
@RestController public class HelloController { @Autowired private RpcService rpcService; /** * 发起远程调用测试 * @return */ @GetMapping("/rpc") public String rpc() { String result = rpcService.hello(); return "发起远程调用,收到返回的信息:" + result; } }
完成以上工程之后,依次将eureka-server
、eureka-provider-1
、eureka-provider-2
、eureka-consumer-feign
服务启动起来。
然后在浏览器上多次访问http://localhost:9003/rpc
,可以得到类似于如下内容。
图片
可以清晰的看到,客户端以轮训的方式调用目标接口。至此,最简单的一个服务注册与调用的例子就完成了。
默认情况下,Fegin 可以满足绝大部分的 HTTP 请求场景。
但是在某些场景下,比如在服务之间实现文件远程上传,如何实现呢?
实际上,Spring Cloud Feign 并不支持直接传文件,但可以通过引入 Feign 的扩展包来实现。
具体实现例子如下。
服务提供方的实现比较简单,按照 Spring MVC 的正常实现即可,文件上传接口示例如下:
@RestController publicclass HelloController { privatestaticfinal String SRC_PATH = "/Users/demo/file/"; @PostMapping("/fileUpload") public String fileUpload(@RequestParam("file") MultipartFile file, @RequestParam("prefixName") String prefixName) throws IOException { // 获取上传文件的文件名 String fileName = file.getOriginalFilename(); String absolutePath = SRC_PATH + prefixName + "_" + fileName; // 将文件保存到磁盘 file.transferTo(new File(absolutePath)); return"Upload file success:" + prefixName + "_" + fileName; } }
在服务消费方,由于需要利用 Feign 客户端来上传文件,需要在pom.xml
文件引入支持文件上传的依赖包,内容如下。
<dependency> <groupId>io.github.openfeign.form</groupId> <artifactId>feign-form</artifactId> <version>3.0.3</version> </dependency> <dependency> <groupId>io.github.openfeign.form</groupId> <artifactId>feign-form-spring</artifactId> <version>3.0.3</version> </dependency>
接着,定义一个文件上传的 Feign 客户端接口,示例如下。
@FeignClient(name = "eureka-provider", configuration = FeignSupportConfig.class) public interface RpcUploadService { @PostMapping(value = "/fileUpload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) String handleFileUpload(@RequestPart(value = "file") MultipartFile file, @RequestParam("prefixName") String prefixName); }
然后,为@FeignClient
注解类创建所需的编码器,也就是上文所配置的FeignSupportConfig
类,不然调用的时候会报错。
@Configuration public class FeignSupportConfig { @Autowired private ObjectFactory<HttpMessageConverters> messageConverters; /** * 微服务传输文件用 * @return */ @Bean public Encoder feignFormEncoder() { return new SpringFormEncoder(new SpringEncoder(messageConverters)); } }
最后,创建一个controller
,通过上文定义的 feign 客户端来上传文件到服务端。
@RestController public class HelloController { @Autowired private RpcUploadService rpcUploadService; @PostMapping("/rpcUpload") public String rpcUpload(@RequestParam("file") MultipartFile file) throws IOException { String result = rpcUploadService.handleFileUpload(file,"feign"); return "通过 feign 发起文件远程上传调用,收到返回的信息:" + result; } }
完成以上操作之后,依次将eureka-server
、eureka-provider-1
、eureka-provider-2
、eureka-consumer-feign
服务启动起来。
用 postman 调用客户端接口上传文件,不出意外的话,会看到类似如下的返回信息。
图片
可以清晰的看到,文件远程上传成功。
最后总结一下,Feign 是一个轻量级的 HTTP 客户端框架,使用者能够以一种更简洁、易于维护的方式来实现 HTTP 服务请求。同时在 Spring Cloud 生态中,Feign 整合了 Ribbon,可以自动实现客户端负载均衡功能。
另外,Feign 还整合了 Hystrix 来实现服务的容错保护,在下一篇文章中我们会对其进行介绍。