【SpringCloud组件——Feign(远程调用)】

news/2024/7/24 5:09:07

前言:

我们在使用Nacos和Eureka的时候都需要使用远程调用开关RestTemplate发送http请求,但是这种方式在代码编写层面太不优雅了,因此我们可以采用Feign来代替RestTemplate发送http请求。

注:此小节同样使用订单系统和用户系统作为代码案例。 

一、RestTemlate和Feign的代码

1.1、RestTemplate

在订单系统调用用户系统的接口时我们之前编写的代码如下:

    public Order queryOrderById1(Long orderId) {
        // 1.查询订单
        Order order = orderMapper.findById(orderId);
        //2.利用RestTemplate发送http请求,查询用户
        //2.1、url路径
        String url = "http://userservice/user/" + order.getUserId();
        //2.2、发送http请求,实现远程调用
        User user = restTemplate.getForObject(url, User.class);
        //3.封装user到order
        order.setUser(user);
        // 4.返回
        return order;
    }

 以上代码存在的问题:

  • 代码可读性差,编程体验不统一
  • 参数复杂URL难以维护

总而言之:太不优雅了~

1.2、Feign 

1.2.1、导入Feign的依赖

        <!--feign客户端依赖-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>

1.2.2、在启动类添加注解开启Feign的功能

@EnableFeignClients(basePackages = "cn.itcast.feign.clients")
public class OrderApplication {
        ......
}

 注解里面的参数我们先不讨论,后续会描述其功能。

1.2.3、编写Feign客户端

@FeignClient(value = "userservice",configuration = DefaultFeignConfiguration.class)//只针对userservice服务有效 全局有效在启动类的注解当中配置即可
public interface UserClient {

    @GetMapping("/user/{id}")
    User findById(@PathVariable("id") Long id);
}

 这里主要是基于SpringMVC的注解来声明远程调用的信息,比如:

 对比我们之前使用的RestTemplate:

1.2.4、使用Feign客户端完成远程调用

    @Autowired
    private UserClient userClient;



    public Order queryOrderById(Long orderId) {
        // 1.查询订单
        Order order = orderMapper.findById(orderId);
        //基于Feign发送http请求(远程调用)
        User user = userClient.findById(order.getUserId());
        //3.封装user到order
        order.setUser(user);
        // 4.返回
        return order;
    }

二、Feign的自定义配置

我们配置Logger来看看:

方式1:基于配置文件的方式

①全局生效

 ②局部生效

 方式2:基于java代码的方式

先声明一个Bean

​public class DefaultFeignConfiguration {
    @Bean
    public Logger.Level logLevel(){
        return Logger.Level.FULL;
    }
}

声明完成后定义该Bean的作用域,即全局生效还是局部生效。

全局生效:在SpringBoot的启动类注解@EnableFeignClients添加如下参数,表明不论远程调用哪个微服务,都将采取配置类当中的配置。

@EnableFeignClients(defaultConfiguration = DefaultFeignConfiguration.class)

局部生效:在Feign客户端的类注解上添加如下参数,表明谁远程调用我这个微服务,将采用这个配置文件的信息。

@FeignClient(value = "userservice",configuration = DefaultFeignConfiguration.class)

 

 三、Feign的性能优化

优化点1:

由于Feign是一个声明式的http客户端,它能做的只是把我们的声明变成http请求,最终发http请求时还是会用到别的客户端,例如一下这几种:

 建立连接池的好处在于可以减少连接创建和销毁的性能损耗。(三次握手、四次挥手)

优化点2:

降低日志的级别,比如使用basic或none。

具体优化方式:

Feign添加httpClient的支持:

  • 引入依赖
        <!--引入httpClient依赖-->
        <dependency>
            <groupId>io.github.openfeign</groupId>
            <artifactId>feign-httpclient</artifactId>
        </dependency>
  • 配置连接池
feign:
  httpclient:
    enabled: true #支持httpClient的开关
    max-connections: 200 #最大连接数
    max-connections-per-route: 50 #单个请求路径的最大连接数

总结:

 四、Feign的最佳实践

方式一(继承):给消费者的FeignClient和提供者的controller定义统一的父接口作为标准。

我们先观察Feign客户端和服务提供者Controller的代码

    @GetMapping("/user/{id}")
    User findById(@PathVariable("id") Long id);
@Slf4j
@RestController
@RequestMapping("/user")
public class UserController {

    @Autowired
    private UserService userService;


    @GetMapping("/{id}")
    public User queryById(@PathVariable("id") Long id) String truth) {
        return userService.queryById(id);
    }

}

可以观察到,两者的内容极其相似,因此我们可不可以让他们两都实现统一的接口?怎么做?

 那么问题又来了,这样又有一些弊端:

服务紧耦合

父接口参数列表中的映射不会被继承

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

先看图:

假设orderservice和payservice将来都调用userservice提供的服务,是不是需要在自己单独的模块当中写一遍userClient,这样一来,随着微服务的数量增加,userClient被写的次数也随之剧增。 解决方案:看图

 将userClient抽取出来组成一个单独的模块,以后不管哪个微服务远程调用时都从这来拿,我把实体类都给你定义了,你只管把我的依赖导入,别的你什么都不用操心,这样,不就皆大欢喜了嘛~

对比两种方式,方式一的弊端在于耦合度的问题,方式二的问题在于,假设我orderservice只需要userClient,但是我把你整个模块的依赖都到导进来了,未免有点小题大作了,我也用不了其他的哪些Client啊~因此,没有完美的解决方案,具体采用哪种还是需要结合实际选择。 

 

 

方式二具体操作:

  • 首先创建一个module,命名为feign-api,然后引入feign的starter依赖

 

  • 将order-service中编写的UserClient、User、DefaultFeignConfiguration都复制到feign-api项目中

 

  • 在order-service中引入feign-api的依赖
        <!--引入feign的统一api-->
        <dependency>
            <groupId>cn.itcast.demo</groupId>
            <artifactId>feign-api</artifactId>
            <version>1.0</version>
        </dependency>
  • 修改order-service中的所有与上述三个组件有关的import部分,改成导入feign-api中的包
    import cn.itcast.feign.clients.UserClient;
    import cn.itcast.feign.pojo.User;

    @Autowired
    private UserClient userClient;



    public Order queryOrderById(Long orderId) {
        // 1.查询订单
        Order order = orderMapper.findById(orderId);
        //基于Feign发送http请求(远程调用)
        User user = userClient.findById(order.getUserId());
        //3.封装user到order
        order.setUser(user);
        // 4.返回
        return order;
    }
  • 重启测试
  • 重启会失败,原因是Spring启动时并没有扫描到创建的这个模块的这些代码,因此也并不会创建他们的实例,怎么办?

 


http://www.niftyadmin.cn/n/353182.html

相关文章

【Docker】- 03 Docker CI、CD(CI、CD、项目配置、环境部署)

Docker CI、CD&#xff08;CI、CD、项目配置、环境部署&#xff09; 1 CICD引言2 CI介绍3 搭建Gitlab服务器3.1 准备工作3.2 修改ssh的22端口3.3 编写docker-compose.yml 4 搭建GitlabRunner5 整合项目入门测试5.1 创建项目5.2 编写.gitlab-ci.yml5.3 将maven工程推送到gitlab中…

开了十年服装店之后,我总结出这9条成功“秘诀”

对于开服装店的老板来说&#xff0c;提高营业额一直都是我们的主要目标。 但是放眼望去&#xff0c;一条街上的服装店随处可见&#xff0c;竞争越来越激烈&#xff0c;任何一家服装店想要经营好&#xff0c;持续不断地吸引顾客上门&#xff0c;都不是一件容易的事。 门店虽小&a…

驱动开发-----io模型总结(2023-5-23)

1.非阻塞模型 在我们使用open函数时&#xff0c;将打开的驱动设置为O_NONBLOCK时&#xff0c;当我们用read函数去读取硬件数据时&#xff0c;无论硬件是否有数据&#xff0c;都会往下执行&#xff0c;不会被阻塞在这里 2.阻塞模型 在我们使用open函数时&#xff0c;没有设置…

Day 50 小结

50.1 比较分析各种查找算法 顺序查找&#xff1a;时间复杂度&#xff1a;O(n)&#xff1b;可用于有序或无序数据&#xff1b;按顺序查找元素。 折半查找&#xff1a;时间复杂度&#xff1a;O(logn)&#xff1b;只能用于有序数据&#xff1b;从中间元素开始查找&#xff0c;每…

网络高并发通信c++库 Muduo

Muduo 是一个基于 C 语言的高性能网络库&#xff0c;它使用了一种被称为 "Reactor" 的异步编程模型。Muduo 库能够帮助开发者轻松实现高并发、低延迟的网络应用程序。该库主要包含以下特性&#xff1a; 1. 使用 C11 语言编写&#xff0c;充分利用了 C11 的新特性。 …

python爬虫之excel解析详解

Excel是一种数据格式化和存储数据的工具&#xff0c;其表格形式非常适合存储和呈现数据&#xff0c;不少企业和业务都使用Excel来进行数据的存储与处理&#xff0c;因此对Excel解析的需求也越来越高。本文主要介绍Python中如何使用openpyxl解析Excel文件&#xff0c;通过一些实…

二叉树的学习

最近在复习数据结构和线性代数&#xff0c;先准备周六的线代考试&#xff0c;好好复习。 目录 树的概念 结点&#xff1a; 空树&#xff1a; 子树&#xff1a; 结点的度&#xff1a; 树的度&#xff1a; 层数&#xff1a; 结点的深度&#xff1a; 结点的高度&#xff…

rust cargo工具常用插件列表

插件功能安装命令advisory检查 Cargo 依赖项中是否存在安全漏洞。cargo install cargo-advisoryasm生成 Rust 代码的汇编版本。cargo install cargo-asmaudit搜索 Rust 代码及其依赖项中的安全漏洞并输出警告。cargo install cargo-auditbenchcmp比较 Rust 基准测试结果。cargo…