SpringCloud 整合 Feign 实现服务远程调用,实战讲解!
一、背景介绍
在上一篇文章中,我们介绍了 Spring Cloud 技术体系中最核心的组件之一 Ribbon,它主要用于是提供客户端的负载均衡算法,帮助服务消费方以某种规则从多个服务提供方中选择一个进行通信。
今天通过这篇文章,结合之前的知识,我们一起来了解一下 Spring Cloud 技术体系中另一个最核心的组件之一 Fegin。
二、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 传输文件
默认情况下,Fegin 可以满足绝大部分的 HTTP 请求场景。
但是在某些场景下,比如在服务之间实现文件远程上传,如何实现呢?
实际上,Spring Cloud Feign 并不支持直接传文件,但可以通过引入 Feign 的扩展包来实现。
具体实现例子如下。
4.1、服务提供方(接收文件)
服务提供方的实现比较简单,按照 Spring MVC 的正常实现即可,文件上传接口示例如下:
@RestController
public class HelloController {
private static final 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;
}
}
4.2、服务消费方(发送文件)
在服务消费方,由于需要利用 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 来实现服务的容错保护,在下一篇文章中我们会对其进行介绍。
六、参考
1.https://www.didispace.com/spring-cloud/spring-cloud-starter-dalston-2-4.html
作者:潘志的技术笔记
出处:https://pzblog.cn/
版权归作者所有,转载请注明出处
