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,});// 拷贝全局 interceptorconst 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独立管理目的

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

imgcat 工具

如果经常在远程服务器或嵌入式设备中操作图片,要查看图片效果,就要先把图片dump到本地,比较麻烦。可以使用这个工具,直接在终端上显示。类似于这种效果。 imgcat 是一个终端工具,使用 iTerm2 内置的特性,允…

加强fou循环的坑

今天遇到了一个有趣的事情,使用加强fou循环操作list时,会报错并发操作异常。 直到看了编译类,才发现,加强fou循环其实就是通过迭代器操作: 这里就会出现一个问题,迭代器在取出值时,就回去检测这…

【数据结构】遍历二叉树(递归和非递归遍历的先序、中序和后序遍历、层次遍历法)

目录 【数据结构】遍历二叉树(递归和非递归遍历的先序、中序和后序遍历、层次遍历法)一、递归算法先(根)序的遍历算法中(根)序的遍历算法后(根)序的遍历算法 二、非递归算法层次遍历…

【漏洞复现】浙大恩特客户资源管理系统 i0004_openFileByStream.jsp 任意文件读取漏洞

0x01 产品简介 浙大恩特客户资源管理系统是一款外贸管理软件,它提供了多种功能,包括客户档案管理、邮件管理、OA外贸办公管理系统、分管权限管理、联系跟进及提醒、业务检查管理、统计分析管理等。 0x02 漏洞概述 浙大恩特客户资源管理系统存在任意文件…

ENSP-旁挂式AC

提醒:如果AC不能成功上线AP,一般问题不会出在AC上,优先关注AC-AP线路上的二层或三层组网的三层交换机 拓扑图 管理VLAN:99 | 业务VLAN:100 注意点: 1.连接AP的接口需要打上pvid为管理vlan的标签 2.AC和…

通用视觉大模型调研

humanbench HumanBench: Towards General Human-centric Perception with Projector Assisted Pretraining;为了解决不同任务之间的conflict以及不同dataset之间的差异(相同任务),提出PATH,backbone是所有任务共享、projector是任务级别共享…

一个开源的全自动视频生成软件MoneyPrinterTurbo

只需提供一个视频 主题 或 关键词 ,就可以全自动生成视频文案、视频素材、视频字幕、视频背景音乐,然后合成一个高清的短视频。 一:功能特性 完整的 MVC架构,代码 结构清晰,易于维护,支持 API 和 Web界面…

DOS命令第二篇

雷迪斯and the乡亲们 欢迎你们来到 奇幻的编程世界 一、echo命令 作用: 输出一个内容到终端 格式: echo 要输出的内容 案例: 直接输出一个“你好” 二、ping 概念: 在网络中通信的时候,主机之间进行通信依靠…

转行或者跳槽入职一家新公司,应该如何快速上手工作?

不管是干测试也好或者其它任何职业,没有谁会在一家公司待一辈子,转行不一定,但是跳槽是每一个打工人早晚都会面临的事情,今天就来跟大家聊聊这件事~ 入职一家新公司,你应该做什么可以最快速的上手工作? 这…

App Inventor 2 如何预览PDF文档?

预览PDF文档的方式 你可以使用Activity启动器查看已存储在你的设备上的 pdf 文档,也可以使用Web客户端通过网址URL打开 pdf 文档。 App Inventor 2 可以使用 .pdf 扩展名从程序包资产中查看 pdf 文件,不再需要外部 pdf 查看器! 代码如下&a…

车载摄像头畸变校正解决方案,打造无畸变高清视界

在车载摄像头日益普及的今天,摄像头图像的畸变问题成为了制约图像质量提升的一大瓶颈。畸变不仅影响画面的美观度,更关键的是它可能导致智能驾驶系统对环境的误判,进而威胁到行车安全。美摄科技凭借其在图像处理领域的深厚实力,推…

中东跨境电商平台Noon注册开店步骤详解

中东地区,素以“满地富豪”闻名,同时拥有发达的电子商务环境与较高的居民消费水平,吸引了大量跨境电商从业者前来寻求商机。其中,Noon作为中东地区颇具人气的电商平台,自然而然成为了众多卖家开拓中东市场的首选平台。…

Flex布局(秒懂弹性盒子的使用)

目录 一、Flex介绍 1.概念 主要概念: 2.Flex容器属性 3.Flex项目属性 4.优势 二、Flex使用 1.弹性盒子内容 2.flex-direction 语法 3.justify-content 属性 4.align-items 属性 语法 5.flex-wrap 属性 语法: 6.align-content 属性 语法&am…

jetson系列开发板使用虚拟机烧录系统时,遇见无法识别开发板的情况

在双系统中的ubuntu系统烧录没问题,但是电脑Ubuntu系统由于版本低,所以没有网络,烧录起来还的连网线,所以问了开发板的工程师,所幸,解决了问题,很感谢工程师的指导,特此记录一下&…

【研发日记】CANoe自动化测试的配置方式(三)——SystemVariables数组方式

文章目录 前言 一、例程功能 二、仿真ECU 三、SystemVariables数组: 四、测试模块 五、测试运行效果 六、分析和应用 总结 前言 近期在做的一个自动化测试项目,尝试了一种以前没用过的测试配置方式,感觉效果还不错。然后又回顾了一下以…

JVM主要知识点详解

目录 1. 性能监控和调优 1.1 调优相关参数 1.2 内存泄漏排查 1.3 cpu飙⾼ 2. 内存与垃圾回收 2.1JVM的组成(面试题) 2.2 Java虚拟机栈的组成 2.3 本地方法栈 2.4 堆 2.5 方法区(抽象概念) 2.5.1 方法区和永久代以及元空…

阿里云服务器8核16G配置最新租用收费价格表与优惠价格

8核16G配置是大部分企业级用户购买阿里云服务器的首选配置,2024年经过调价之后,8核16G配置的阿里云服务器按量收费标准最低为0.9元/小时,按月租用平均优惠月价最低收费标准为432.0元/1个月,按年购买最低活动价格为1803.17元/1年&a…

Avalonia中嵌入网页程序(CefNet)

Avalonia中嵌入网页程序cefNet 1. 引入CefNetNuget包2. 下载 cef 基础环境3. 将cef基础环境放入程序运行目录下4. 代码中初始化cef5. 添加Webview控件6. 在窗口关闭的时候释放Cef7. 项目结构图CefNet 开源的作者已经停止维护并删除了原始的代码库:GetHub:CefNet,Nuget上还有发…

# ABAP SQL 字符串处理-CONCATCAST

经常我都要在ABAP的sql语句中对字符串进行处理,现在就总结一下可以用到的方法 文章目录 字符串处理拼接字段运行结果 填充字符串运行结果 截取字符串 SUBSTRING运行结果 CAST转换类型程序运行结果 CAST 转换成 DATS类型(日期) 字符串处理 在…

【c语言】结构体的访问

🎈个人主页:豌豆射手^ 🎉欢迎 👍点赞✍评论⭐收藏 🤗收录专栏:C语言 🤝希望本文对您有所裨益,如有不足之处,欢迎在评论区提出指正,让我们共同学习、交流进步&…