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循环其实就是通过迭代器操作: 这里就会出现一个问题,迭代器在取出值时,就回去检测这…

elementUi el-date-picker时间控件精确到时分秒

1.时间日期typedatetime <template><el-date-pickerv-model"time":picker-options"pickerOptions"ref"datetime"type"datetime":default-time"range":disabled"disabled"placeholder"请选择日期时…

Spring Boot 定时任务实现教程

前言 在实际开发过程中&#xff0c;我们经常需要处理一些周期性或定时执行的任务&#xff0c;如数据备份、报表生成、邮件发送等。Spring Boot通过集成Quartz或使用Spring自带的Scheduled注解可以方便地实现定时任务功能。本文将详细介绍如何在Spring Boot项目中使用Scheduled…

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

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

存储器管理

01.存储器的层次结构&#xff1a;计算机在执行指令时&#xff0c;几乎每条指令都会涉及对存储器的访问&#xff0c;因此要求计算机对存储器的访问速度能跟得上处理机的运行速度&#xff0c;在现代计算机系统中&#xff0c;都采用了多层结构的存储器。至少应该有三层&#xff1a…

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

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

ENSP-旁挂式AC

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

通用视觉大模型调研

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

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

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

【面试亮点】线上GC问题排查止损解决 (heap space OutOfMemory排查止损解决)

【面试亮点】线上GC问题排查&止损&解决(heap space OOM排查&止损&解决) 许多同学总和我抱怨说面试的时候没有线上实际排查解决gc问题的经验,我这里分享我团队的一次比较好的从 发现问题->及时止损->排查问题->修复问题->复盘 全流程的实践经验,希…

DOS命令第二篇

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

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

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

App Inventor 2 如何预览PDF文档?

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

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

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

redis清理缓存接口开发

文章目录 1 用户注册1.1 简要描述1.2 请求URL1.3 请求方式1.4 参数1.5 返回示例1.6 返回参数说明1.7 备注 2 用户登录2.1 简要描述2.2 请求URL2.3 请求方式2.4 参数2.5 返回示例2.6 返回参数说明2.7 备注 3 权限校验3.1 简要描述3.2 请求URL3.3 请求方式3.4 参数3.5 返回示例3.…

leetcode刷题(python)——(一)

01.01.04 练习题目&#xff08;第 01 天&#xff09; 1. 2235. 两整数相加 1.1 题目大意 描述&#xff1a;给定两个整数 n u m 1 num1 num1 和 n u m 2 num2 num2。 要求&#xff1a;返回这两个整数的和。 说明&#xff1a; − 100 ≤ n u m 1 , n u m 2 ≤ 100 -100 \l…

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

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

普通类的成员函数模板

4-4普通类的成员函数模板、类模板的成员函数模板、&#xff08;c11&#xff09;模板显式实例化&#xff0c;模板声明_普通类的模板函数能否为虚函数-CSDN博客 实例&#xff1a; #include <QApplication> #include <QDebug> struct user_inform {QString user_name;…

牛客 接头密匙

Problem: 牛客 接头密匙 文章目录 思路解题方法复杂度Code 思路 这个问题可以通过使用前缀树&#xff08;Trie&#xff09;来解决。前缀树是一种用于存储字符串的数据结构&#xff0c;其中每个节点代表一个字符串的前缀。在这个问题中&#xff0c;我们可以使用前缀树来存储数组…