微服务feign接口声明的3种方式使用与分析

前言

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 {

    @Autowired
    private 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}")
    @Override
    public 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的最佳实践方式。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值