服务远程调用组件 Spring Cloud Feign 架构原理及用法

一、背景介绍

Spring Cloud 技术体系中最核心的组件之一 Ribbon,它主要用于是提供客户端的负载均衡算法,帮助服务消费方以某种规则从多个服务提供方中选择一个进行通信。今天通过这篇文章,结合之前的知识,我们一起来了解一下 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
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值