Module Federation微前端应用拆分后 - request请求优化、私有化request|分发拦截器

1. 背景及目的

1.1 需求背景

随着应用的拆分,目前子应用有12个,这些子应用都使用的是同一个request实例。

前端支持后端切流,增加多个拦截器用于灰度

经手动梳理:

目前所有应用中有26个在使用的拦截器,
其中用于灰度切流的拦截器有13个,
Request 16个
Response 10个

各个模块使用到的拦截器及是否可以下线的详情看所有应用中拦截器使用情况

1.2 现状分析 及 问题

根据背景我们发现目前 request为单实例,这个request实例在主应用创建并添加到window.APP_CONTEXT中暴露给子应用获取,所有应用使用的都是同一个request实例。

且 interceptor修改没有加限制,可能会造成下列的问题:

1.2.1 增加接口rtt耗时:

前端为了支持后端接口切流使用了20+个拦截器去做灰度处理,每个接口的请求,都要经过20+个拦截器逻辑,增加接口请求、响应耗时。

1.2.2 范围模糊:

这些拦截器有些写在主应用,有些写在子应用,即使接口只需要子模块范围内拦截的,也会作用到所有模块。

1.2.3 不可控:

随着应用的拆分,目前1个主应用,12个子应用使用的都是同一个request实例。

任一应用对request的修改会影响其他所有应用,且其中有部分应用是跨团队在维护,增加了request的不可控性。

1.3 需求目的

  • 梳理现状
  • 整理现有 interceptors,做好归类以及标志,为后续的解决方案提供参考材料
  • 探索有效解决 Request-Intercepor 现有问题、可行性较高 且 改造成本低的优化方案

2. 整体设计

通过源码阅读得知:Interceptor 的绑定及执行逻辑

2.1 目前request绑定interceptor情况

2.2.1 主应用初始化过程中,生成Request实例之后

在main.js 加载时调用.

2.2.2 子应用初始化过程中

在lazy-module中调用拦截器注册函数

在initModule中MF提供的afterRegisteration(所有子模块注册后)与beforeRegisteration(所有子模块注册前)方法中调用.

2.2.3 子应用实际发起请求时

根据使用情况use拦截器

无论什么时机调用拦截器,影响的是挂载拦截器,拦截器生效时机,最终还是作用于全局。
在这里插入图片描述

2.2 目前的request实例可能会出现的interceptor使用情况

a.主应用内使用主应用内定义的interceptor

b.子应用调用子应用内部的interceptor

c.子应用调用主应用定义的interceptor

c.子应用间互相调用interceptor
在这里插入图片描述

2.3 目标: 优化后request使用情况

2.3.1 Request 实例独立

加载时机

主应用:主应用初始化,使用主应用生成request实例的方法,生成request实例。request实例挂载interceptor

子应用:进入子模块a,拿到只属于本模块requestA实例。requestA实例挂载interceptor.

卸载时机

无需卸载,离开子模块a,进入子模块b后,使用的是子模块b的拦截器。

2.3.2 Interceptor 独立

a. 主应用内使用主应用内定义的interceptor

b.子应用调用子应用内部的interceptor

c.子应用可以调用主应用共享出来的interceptor,私有的无法访问

d. 子应用间可以实现互相调用interceptor

3. 详细设计

3.1 思路一: 私有化request实例

  • 提供生成request实例的方法,使各子应用只能基于主应用提供的方法生成自己应用内的实例。
  • 子应用间request实例互不影响
  • 子应用只能修改模块内部的interceptor

私有变量方式:提供生成request实例的方法,使各子应用只能基于主应用提供的方法生成自己应用内的实例

主应用:

import axios from 'axios';

// 通过内部变量
let _instance = null;

const baseURL = '';
const defaultConfig = {
  baseURL,
};

class Request {
  constructor() {
    _instance = axios.create(defaultConfig);

    // 全局 interceptor
    _instance.interceptors.request.use(
      () => {},
      () => {}
    );
    _instance.interceptors.response.use(
      () => {},
      () => {}
    );
  }

  async doRequest(config) {
    console.log(config.method);
    try {
      const response = await _instance(config);
      return response;
    } catch (e) {
      // error report
    }
  }

  get() {
    return this.doRequest({
      method: 'get',
    });
  }

  post() {
    return this.doRequest({
      method: 'post',
    });
  }
  // 子模块通过主应用 Request 实例 提供的方法进行新的 Request 孵化
  incubation(config) {
    const request = axios.create({
      ...defaultConfig,
      ...config,
    });

	// 拷贝全局 interceptor
    const baseRequestInterceptor = _instance.interceptors.request.handlers || [];
    baseRequestInterceptor.forEach((i) => {
      request.interceptors.request.use(i.fulfilled, i.rejected);
    });
    const baseResponseInterceptor = _instance.interceptors.response.handlers || [];
    baseResponseInterceptor.forEach((i) => {
      request.interceptors.response.use(i.fulfilled, i.rejected);
    });
    return request;
  }
}

Request.getInstance = function () {
  if (!this.instance) {
    this.instance = new Request();
  }
  return this.instance;
};

export const request = Request.getInstance(); 

/*
* -----------
* expose function
* -----------
*/
export const NewRequest = Request.incubation;

子应用:

/*
 * -----------
 * call expose function NewRequest to get a new request instance
 * -----------
*/
const request = getAppCtx().NewRequest();
  • 子模块不能添加全局 interceptor,子应用只能修改模块内部的interceptor
    删除现有expose到挂载在window下的APP_CONTEXT中的request实例,子应用无法直接操作主应用的request实例

基于以上思路 进行修改,经验证方法可行:

主应用页面中,子应用的interceptor没有执行。

子应用中,子应用中绑定的interceptor和主应用分享出来的interceptor被执行.

  • 子应用间交叉依赖
    在这里插入图片描述

由于是多实例,可能会出现子应用a会调用子应用b中的业务接口。若该业务接口在b中有拦截器对之进行处理,子应用a也需要使用该拦截器。

上面的修改无法兼容子应用间交叉依赖的场景。

为了达到子模块只能修改本模块的request interceptor,但又要互通,对此,可以考虑通过主应用生成对应模块的request,只派发该模块的request实例。通过map[module]=requestInstance的方式存储管理。

若子模块a需要使用子模块b的interceptor,则子应用告诉主应用需要哪个模块的request实例,由主应用派发模块b的request实例给模块a使用。如上图所示

子应用间交叉依赖的场景比较少见,且是不规范行为,此方案只为兼容特殊场景。

为兼容上述场景,将做下面的调整

  1. 基于当前已有的逻辑,通过主应用以私有变量方式,生成每个模块的实例。当模块修改其request实例时,主应用中的moduleRequest管理每个module对应的request实例

主应用:

Request.incubation = function (module) {
/*
* 当需要用到其他模块的reuqest时,直接返回已有的request实例
*/   
if(this.moduleRequest[module]) return this.moduleRequest[module];

  const mainRequest = Request.getInstance();
  const instance = new Request();
  const baseRequestInterceptor =
    (mainRequest.axiosInstance.interceptors.request && mainRequest.axiosInstance.interceptors.request.handlers) || [];
  baseRequestInterceptor.forEach((i) => {
    instance.getAxiosInstance().interceptors.request.use(i.fulfilled, i.rejected);
  });
  const baseResponseInterceptor =
    (mainRequest.axiosInstance.interceptors.response && mainRequest.axiosInstance.interceptors.response.handlers) || [];
  baseResponseInterceptor.forEach((i) => {
    instance.getAxiosInstance().interceptors.response.use(i.fulfilled, i.rejected);
  });
  this.moduleRequest[module] = instance;
  return this.moduleRequest[module];
}; 
export const NewRequest = Request.incubation;

子应用使用

const request = getAppCtx().NewRequest('instation');
const requestOtherModule = (otherModule) => getAppCtx().NewRequest(otherModule);//业务中用到时才调用,防止获取时早于模块的初始化
  1. 当子模块正常使用时
request.get('/api/a-scope/...')
  1. 当子模块需要依赖其他模块时
requestOtherModule('b').get('/app-b/...')

3.2 思路二:共用request同一实例,通过来源分发拦截器

各域给request传入自己的config标识,主应用通过判断标识区分来源

  1. 通过interceptors.xx.use中提供的options runWhen + 获取子应用传入request的标识,判断是否需要绑定拦截器到request实例中。

![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/ae4f5439fbdc4649a11780e6fbd0d307.png
然而 添加runWhen属性需要开发的自觉性,可以被跳过

且 项目中的axios版本没有升级,没有新加的synchronous与runWhen属性,若需要使用,要考虑升级版本.
项目中的axios包代码

  1. 通过来源加载、卸载拦截器

基于 ,目前已有可以获取当前页面来源于哪个模块的支持。

3.3 思路三:改造Axios

将axios执行拦截器的方式由推进队列改为用map对应key执行key中的拦截器。key为子应用标识。

方案权衡:

思路优劣势
思路一: 私有化request实例,子应用间request实例互不影响优势:满足各域拦截器私有化问题满足各域request独立管理目的。劣势:多实例下,子应用间互相依赖时,使用者需要额外说明多实例风险较高,可能会存在没有意识到的问题
思路二:共用request同一实例,通过来源分发拦截器优势: 满足各域拦截器私有化问题逻辑简单,修改起来方便。劣势:只解决了拦截器绑定的问题,没有解决request的独立管控问题,目前项目中的axios没有支持runWhen,需要查看依赖包版本
思路三:改造Axios优势:满足各域拦截器私有化问题 。劣势:改造成本较高不满足各域request独立管理目的

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/546825.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

hive了解系列一

“ 随着智能手机的普及,互联网时代红利的爆发,用户数量和产生的数据也越发庞大。为了解决这个问题,提高数据的使用价值。 Hadoop生态系统就被广泛得到应用。 在早期,Hadoop生态系统就是为处理如此大数据集而产生的一个合乎成本效益…

【JAVA基础篇教学】第十四篇:Java中设计模式

博主打算从0-1讲解下java基础教学,今天教学第十四篇:Java中设计模式。 设计模式是解决软件设计中常见问题的可重复利用的解决方案。在 Java 中,常见的设计模式包括单例模式、工厂模式、观察者模式等。目前在基础教学篇中只展示常见的几种模…

nvm node.js的安装

说明:部分但不全面的记录 因为过程中没有截图,仅用于自己的学习与总结 过程中借鉴的优秀博客 可以参考 1,npm install 或者npm init vuelatest报错 2,了解后 发现是nvm使用的版本较低,于是涉及nvm卸载 重新下载最新版本的nvm 2…

【RabbitMQ】RabbitMQ基础认识

文章目录 前言初识MQSpringAMQP如何首发消息?消费者交换机Fanout:广播Direct交换机Topic交换机声明队列和交换机 总结 前言 微服务一旦拆分,必然涉及到服务之间的相互调用,目前我们服务之间调用采用的都是基于OpenFeign的调用。这…

四川古力未来科技抖音小店安全:保障您的购物体验,让每一笔交易都安心

在数字化浪潮席卷全球的今天,电子商务已经成为人们生活中不可或缺的一部分。四川古力未来科技抖音小店,作为新兴的电商力量,始终将顾客的安全放在首位,倾力打造安全、便捷、高效的购物平台,让每一位顾客在享受购物乐趣…

计算机网络(五)传输层

传输层 从通信和信息处理的角度看,传输层向它上面的应用层提供通信服务,属于面向通信部分的最高层,同时也是用户功能中的最低层 传输层功能: 传输层提供应用进程之间的逻辑通信(即端到端的通信)。与网络层的区别区别是&#xf…

Python进阶编程 --- 2.MySQL、pymysql、PySpark

文章目录 第一章:SQL基础入门1.1 数据库数据库如何存储数据 1.2 数据库和SQL的关系1.3 MySQL版本1.4 命令提示符内使用MySQL1.5 SQL概述1.5.1 SQL语言分类1.5.2 SQL语言特性 1.6 DDL库管理表管理 1.7 DML - 数据操作1.8 DQL - 查询和计算数据1.8.1 基础数据查询1.8.…

【opencv】示例-videowriter_basic.cpp从默认摄像头视频采集和录制

这段代码的功能是使用OpenCV从默认摄像头捕获视频流,并将这些视频流实时写入到一个名为live.avi文件中。视频流以MJPG编码格式被写入,帧率设置为25帧每秒。程序还会通过一个窗口实时显示摄像头捕获的画面,窗口标题为"Live"。用户可…

6.GodotCanvasItem、Node2D及自定义节点

CanvasItem节点 CanvasItem节点,CanvasItem -> Node,所以CanvasItem继承了Node的所有功能Canvas是画布的意思,所以CanvasItem代表了就是可以被绘制的节点,可以设置可视化界面和材质的颜色所有的2D节点和GUI节点都继承于CanvasI…

科技云报道:AI大模型疯长,存储扛住了吗?

科技云报道原创。 AI大模型正在倒逼数字基础设施产业加速升级。 过去一年半,AI大模型标志性的应用相继出现,从ChatGPT到Sora一次次刷新人们的认知。震撼的背后,是大模型参数指数级的增长。 这种数据暴涨的压力,快速传导到了大模…

Vue 指令

Vue根据不同的指令&#xff0c;针对标签实现不同的功能 指令&#xff1a;带有v-前缀的特殊的标签属性 <!-- Vue指令--> <div v-html"str"></div><!-- 普通标签属性 --> <div class"box"></div> 目录 v-html v-sho…

Linux的学习之路:11、地址空间

摘要 本章主要是说一下地址空间&#xff0c;我也只是按照我的理解进行解释&#xff0c;可能说不清楚&#xff0c;欢迎指正 目录 摘要 一、空间布局图 二、代码测试一下 三、进程地址空间 四、测试代码 一、空间布局图 如下方图片可以看出地址空间有几种&#xff0c;这里…

论文笔记:Time Travel in LLMs: Tracing Data Contamination in Large Language Models

iclr 2024 spotlight reviewer评分 688 1 intro 论文认为许多下游任务&#xff08;例如&#xff0c;总结、自然语言推理、文本分类&#xff09;上观察到的LLMs印象深刻的表现可能因数据污染而被夸大 所谓数据污染&#xff0c;即这些下游任务的测试数据出现在LLMs的预训练数据…

java的深入探究JVM之内存结构

前言 Java作为一种平台无关性的语言&#xff0c;其主要依靠于Java虚拟机——JVM&#xff0c;我们写好的代码会被编译成class文件&#xff0c;再由JVM进行加载、解析、执行&#xff0c;而JVM有统一的规范&#xff0c;所以我们不需要像C那样需要程序员自己关注平台&#xff0c;大…

实景三维技术在公共安全领域的应用

随着科技的不断发展&#xff0c;实景三维技术在公共安全领域的应用越来越广泛。实景三维技术是指通过采集现实世界的三维数据&#xff0c;构建出真实的三维场景&#xff0c;进而实现对现实世界的数字化模拟和重建。在公共安全领域&#xff0c;实景三维技术的应用不仅可以提高安…

《云原生安全攻防》-- 云原生攻防矩阵

在本节课程中&#xff0c;我们将开始学习如何从攻击者的角度思考&#xff0c;一起探讨常见的容器和K8s攻击手法&#xff0c;包含以下两个主要内容&#xff1a; 云原生环境的攻击路径: 了解云原生环境的整体攻击流程。 云原生攻防矩阵: 云原生环境攻击路径的全景视图&#xff0…

服务器负载均衡SLB/加密原理

多台服务器提供相同的服务 SLB(server load balancing) 多台服务器对应一个虚拟地址&#xff0c;该地址是防火墙虚拟出来的。 服务器负载均衡功能仅支持IPV4协议 多通道协议仅支持FTP协议

逆向IDA中Dword,数据提取

我们可以看见数据是这样的&#xff0c;第一个是1cc 但是我们shifte就是 这个因为他的数据太大了&#xff0c;导致高位跑后面去了 这个时候&#xff0c;我们右键——convert——dword 这样就可以提取到争取的数据了 比如第一个数据 0x1cc a0xcc b0x1 print(hex((b<<8…

M系Mac关闭SIP

文章目录 M系Mac关闭SIP一&#xff1a;查看SIP状态二&#xff1a;关闭SIP步骤 M系Mac关闭SIP 一&#xff1a;查看SIP状态 1、使用终端 打开终端 输入csrutil status&#xff0c;回车 你会看到以下信息中的一个&#xff0c;指示SIP状态 已打开 System Integrity Protection s…

C#引用外部组件的常用方法

我们在开发程序过程中&#xff0c;时常会使用到第三方组件&#xff0c;比如一些通信、UI组件等。常用的引用方法有下面几种。 01 NuGet引用 NuGet是.NET的一个包管理平台&#xff0c;很多开源组件会通过NuGet进行管理和发布。比如我们常用的S7NetPlus等。 从NuGet中引用组件…