OpenFeign 实现远程调用

1. 问题引入

在前面写的代码中,通过使用RestTemplate来实现远程调用,但是这种方式还是存在一些问题

  1. 需要拼接 URL,灵活性高,但是 URL 复杂时容易写错
  2. 代码可读性差

而 OpenFeign 就解决了上述问题,OpenFeign 是一个声明式的 Web Service 客户端,用于简化在 Java 应用程序中调用 RESTful 服务的过程,也就是可以使用接口和注解来定义和调用 RESTful 服务,而不是手动编写 HTTP 请求代码。这种方式使得代码更简洁、易读,易于维护。

2. OpenFeign 的使用

首先,由于是 order-service 进行远程调用 product-service,需要在 order-service 中引入 openfeign 的依赖

<dependency>
  <groupId>org.springframework.cloud</groupId>
  <artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>

然后需要在 order-service 的启动类上添加@EnableFeignClients注解,开启 OpenFeign 的功能

@EnableFeignClients
@SpringBootApplication
public class OrderServiceApplication {
    public static void main(String[] args) {
        SpringApplication.run(OrderServiceApplication.class,args);
    }
}

然后来定义一个 Feign 客户端接口:

@FeignClient(value = "product-service",path = "/product")
public interface ProductApi {

    @RequestMapping("/{productId}")
    ProductInfo getProductById(@PathVariable("productId") Integer productId);
}

通过@FeignClient注解来定义对远程服务的调用,value 指的是要调用的服务名称,path 指的是 Feign客户端 的统一路径

接着就可以通过之前的方式来注入,再调用方法

这样就完成了远程调用

3. 参数传递

在上面的代码中,演示的是从 URL 中获取参数,接下来演示 Feign 传递参数的方式

传递单个参数和传递多个参数都是通过@RequestParam来进行参数绑定

@RequestMapping("/p1")
String product1(@RequestParam("id") Integer id);

@RequestMapping("/p2")
String product2(@RequestParam("id") Integer id,@RequestParam("name") String name);

传递对象是通过@SpringQueryMap注解来把对象作为多个参数传递的

@RequestMapping("/p3")
String product3(@SpringQueryMap ProductInfo productInfo);

传递 JSON 还是用的@RequestBody注解来实现的

@RequestMapping("/p4")
String product4(@RequestBody ProductInfo productInfo);

4. 最佳实践

从上面的代码中可以看出,Feign 客户端和服务提供者的 controller 代码非常相似,可以通过下面的这两种方式进行简化

4.1. 继承方式

上面是官方给出的继承的方式,来看具体怎么实现:

接口可以放在一个公共的 jar 包中,这样服务提供方和客户端都能够使用

然后把公共实体类和要被继承的接口都写在模块中

然后把当前模块打成 jar包,

让 order-service 和 product-service 分别去引入 jar 包

然后让服务提供方的 controller 实现 ProductInterface:

服务消费方去继承该接口

4.2. 抽取方式

首先还是把 Feign 的 Client 抽取为一个独立的模块,并把涉及到的实体类等都放在这个模块中,然后打成 jar 包,服务消费方只需要引入依赖即可

抽取的部分和上面还是一样的

引入依赖之后就可以直接调用,不用再写继承了,只需要在启动类上添加扫描路径,有两种方式可以添加扫描路径,一种是使用 basePackages 直接指定要扫描的路径,另一种是通过 clients 来指定具体的客户端接口类

@EnableFeignClients(basePackages = "com.example.feignclients")
@SpringBootApplication
public class OrderServiceApplication {
    public static void main(String[] args) {
        SpringApplication.run(OrderServiceApplication.class, args);
    }
}
@EnableFeignClients(clients = {ProductApi.class})
@SpringBootApplication
public class OrderServiceApplication {
    public static void main(String[] args) {
        SpringApplication.run(OrderServiceApplication.class,args);
    }
}

之后就可以成功调用了

5. 部署

由于项目中引用的是一个本地的依赖,所以打包的配置和之前又有些不一样了,首先需要指明需要从本地获取 jar 包

在 order-service 中引入依赖时指定一下要获取的 jar 包的路径

在插件中也配置一下,表示包含系统范围的依赖

之后就可以打包上传了