微服务实战:服务远程调用组件 Spring Cloud Feign 架构原理及用法,实战讲解!

Feign 是一个轻量级的 HTTP 客户端框架,使用者能够以一种更简洁、易于维护的方式来实现 HTTP 服务请求。同时在 Spring Cloud 生态中,Feign 整合了 Ribbon,可以自动实现客户端负载均衡功能。
首页 新闻资讯 行业资讯 微服务实战:服务远程调用组件 Spring Cloud Feign 架构原理及用法,实战讲解!

一、背景介绍

今天通过这篇文章,结合之前的知识,我们一起来了解一下 Spring Cloud 技术体系中另一个最核心的组件之一 Fegin。

二、Fegin 简介

Spring Cloud Feign 是一套基于 Netflix Feign 实现的 HTTP 客户端工具,主要作用是简化 HTTP 客户端的开发和维护工作。

传统的模式下,当我们要对某个接口发起 HTTP 请求时,首先会封装 HTTP 请求报文,然后发起请求,最后处理响应结果。例如之前介绍过的RestTemplate工具。

其实这三步骤,可以编写一个动态代理类来帮助我们以一种更简洁、易于维护的方式完成 HTTP 请求的调用。Fegin 的实现逻辑大体就是这种思路,我们只需要创建一个接口并添加@FeignClient注解,然后配置相关的请求方法既可完成 HTTP 请求工作,剩下的就交给代理类来完成。不过底层,使用的依然是Apache HttpClientOkHttp发送请求。

与原生 Feign 组件相比,Spring Cloud Feign 还扩展了对 Spring MVC 注解的支持,同时还整合了 Ribbon 提供客户端的负载均衡实现,以及 Hystrix 服务熔断器。

下面我们通过具体的例子,看看如何使用 Feign 来实现 HTTP 请求。

三、方案实践

与之前介绍 Ribbon 类似,依次创建eureka-servereureka-provider-1eureka-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-servereureka-provider-1eureka-provider-2eureka-consumer-feign服务启动起来。

然后在浏览器上多次访问http://localhost:9003/rpc,可以得到类似于如下内容。

图片图片

可以清晰的看到,客户端以轮训的方式调用目标接口。至此,最简单的一个服务注册与调用的例子就完成了。

四、Fegin 传输文件

默认情况下,Fegin 可以满足绝大部分的 HTTP 请求场景。

但是在某些场景下,比如在服务之间实现文件远程上传,如何实现呢?

实际上,Spring Cloud Feign 并不支持直接传文件,但可以通过引入 Feign 的扩展包来实现。

具体实现例子如下。

4.1服务提供方(接收文件)

服务提供方的实现比较简单,按照 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;
    }
}

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-servereureka-provider-1eureka-provider-2eureka-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

31    2025-03-07 08:57:46    HTTP 客户端 框架