微服务feign接口声明的3种方式使用与分析
创始人
2024-04-08 23:57:20

前言

feign调用方式是微服务调用最为广泛的使用方式,feign接口声明位置也是比较关键的一环。目前来说,feign的3种接口声明方式各自存在利弊,并不存在最优解决方案,只能根据需求去选择。本文中不作详细项目搭建过程,但并非无码之谈,阅读前请认真阅读项目说明。

项目说明

案例项目中有两个项目,分别是用户(user-service-nacos)与订单(order-service-nacos)。用户是服务提供者,订单是消费者。
user-service-nacos中只有一个接口findById,返回一串随机数

@RestController
public class UserController {@GetMapping("/user/{id}")public String findById(@PathVariable("id") String id) {Random r = new Random();return "user/id: " + id + r.nextInt();}}

order-service-nacos有一个接口会去调用user-service-nacos的findById

@RestController
public class OrderController {@Autowiredprivate UserClient userClient;@GetMapping("/order/{id}")public String findById(Long id) {String byId = userClient.findById("9");return "userClient return value : " + byId;}
}

代码很简单,我们要探讨的问题就是上面的UserClient的feign接口声明放置的位置的用法与利弊

方式一:消费者中单独声明

在order-service-nacos中单独声明一个UserClient接口

@FeignClient("user-service")
public interface UserClient {@GetMapping("/user/{id}")String findById(@PathVariable("id") String id);}

调用结果:
在这里插入图片描述

优劣分析

使用在消费者单独声明的方式,写法简单,代码耦合度低,可用于不处于同一个工程下的应用。但存在问题也是很明显,需要维护两个地方一模一样的代码,订单服务中的UserClient的声明其实和UserController声明的接口是一模一样的,而且必须要一模一样的,这就造成了代码冗余和维护的不便。

方式二

基于方式一的探讨,我们很容易想到,一模一样的代码,而且冗余,那解决方案很简单呀,抽方法,定义一个公共接口,让服务者与消费者都继承接口。
定义一个公共的接口:UserAPI

public interface UserAPI {@GetMapping("/user/{id}")String findById(@PathVariable("id") String id);
}

订单服务中,UserClient直接继承了这个接口,方法不用手动编写

@FeignClient("user-service")
public interface UserClient extends UserAPI {}

用户服务中,UserController去实现接口中的方法

@RestController
public class UserController implements UserAPI {@GetMapping("/user/{id}")@Overridepublic String findById(@PathVariable("id") String id) {Random r = new Random();return "user/id: " + id + r.nextInt();}}

优劣分析

这样的做法有效避免了方式一中的缺陷,但由于提供者与消费者共用同一个接口,造成了紧耦合关系,如果UserAPI接口发生变动,提供者与消费者两端都受到影响,也无法作用于隔离开的两个工程项目。而且spring mvc继承的方式无法继承参数映射的注解,如上面的@PathVariable。尽管这种方式有缺点,但是也是遵循了面向契约的编程思想,在企业中有广泛的使用。

提出问题

假设有很多个微服务都要调用用户服务,那岂不是要在各个服务中都定义一个UserClient?如果在同一个父级pom文件下,你会想到将UserClient抽出来作为一个单独的module,这就是方式三的基本思想。

方式三:抽取独立模块

将FeignClient抽取为独立模块,并且把接口相关的实体类(pojo)、默认的Feign配置都放到这个模块中,提供给所有消费者使用。

创建一个单独module,引入feign依赖,将UserClient复制到此处。我这里的案例比较简单,没有pojo,如果说接口返回的是一个对象,那么这个对象应该声明在下面pojo包中(也可以是dto)。其它地方使用时,需要导入该模块,注意配置好springboot的包路径扫描。
在这里插入图片描述
通常来说,feign-api模块最好是放置在服务提供者工程中,方便管理。若提供者与消费者属于同一个工程,则可直接引入pom文件。否则,则需要执行maven的package命令将feign-api打成jar包才能被消费者使用。

优劣分析

这种方式有效降低耦合度,并减少重复开发的工作,但也存在一定问题,假设定义的feign-api module中有大量的接口类,消费者只需要其中一两个接口,但是导入了比较多的类,存在冗余。相对而言,这种方式是feign的最佳实践方式。

相关内容

热门资讯

2月6日创业板新能源ETF工银... 2月6日,创业板新能源ETF工银(159149)涨1.24%,成交额625.72万元。当日份额增加1...
2月6日科创芯片ETF华宝(5... 2月6日,科创芯片ETF华宝(589190)跌0.97%,成交额2406.38万元。当日份额减少30...
1... 11.11促销正如火如荼地进行中,想买电脑的朋友肯定不少。 那么应该如何选择主板呢? 当然,不能因为...
电... 如今,计算机已经成为大多数人生活和工作中不可或缺的工具。 随着软件的更新和升级,电脑软件也越来越完善...
乐... 乐视电视外观简洁大方,内部“配料”品质十足,既美观又强大。 用户使用乐视电视最大的需求就是观看高清直...