为了实现接口缓存,专门写了个缓存库 f-cache-memory
问题起因
起因是某次发版之后,服务器接口压力过大,当场宕机,排查之后发现有个接口在首页被调十来次(六七年的老项目了,都是泪呀),后端反馈这个接口的sql很复杂,很耗性能,临时把这个接口放到登录后只执行一次,数据缓存在 localStorage 内,后续这个接口都直接从 localStorage 中取。
虽然临时解决了宕机问题,但还是会有多个组件内同时发起多个相同请求的问题。现在也有很多请求库带有缓存功能,但是这是老项目,请求只封装了 axios ,换个请求库风险又很高,最终决定自己搞搞。
思路历程
当时最初的想法就是在 axios 拦截器内做判断,首先在 interceptors.request 中判断缓存中是否有对应的请求,在 interceptors.response 中赋予缓存设置。
但很快问题就来,在 interceptors.request 判断有缓存之后,再取消这个缓存吗?搞了搞又发现个问题,当某个请求已发起,但是还没返回,这个时候这个请求又发起了,这不没解决问题。
然后又想着在 interceptors.request 中先让请求占位,这样多个组件同时发起某个请求,只要第一个占位了,后续的都取消。问题又来了,后续请求取消之后,后续逻辑又不触发了。后面突然想到我可以在请求之前做检测,当对应接口已经在缓存中时,就直接返回当前缓存中的值,缓存不存在再发起,这个时候肯定会想那第一个已经发起的还未返回的,后续相同接口从缓存中拿到的值如何出发后续逻辑呢?
请求本身返回的是 promise,那我先把 promise 存入缓存,相同接口再请求时直接返回缓存中的 promise ,这样后续逻辑可以正常触发。
最终方案
最终实现如下(仅 get 请求做接口缓存):
export function get<T = any>(url: string, config: AxiosRequestConfig = {}): Promise<T> {const curHttpCacheKey: string = configToKey({url,...config})if (!httpCache.hasCache(curHttpCacheKey)) {const httpRequest = instance.get(url, config)httpCache.setCache(curHttpCacheKey, httpRequest)return httpRequest as Promise<T>} else {return Promise.resolve(httpCache.getCache(curHttpCacheKey))}
}
对 get 请求再做个封装,缓存中以请求链接和 query拼接的字符串作为 key,httpCache 后续会讲, configToKey 实现如下:
export function configToKey(config: AxiosRequestConfig): string {let key = config.url as stringif (config.params) {key += JSON.stringify(config.params)}return key
}
然后在 interceptors.response 中赋予缓存值:
instance.interceptors.response.use((response) => {if (response.status === 200 && response.config.method === 'get') {const curHttpCacheKey = configToKey(response.config)// 缓存中设置的值要和下面 return 的结果一致httpCache.setCache(curHttpCacheKey, response)}return response},(error) => {return Promise.reject(error)}
)
下面说下 httpCache:
import CacheMemory from 'f-cache-memory'const httpCache = new CacheMemory()
export default httpCache
缓存库 f-cache-memory
初始化一个库就行了,f-cache-memory 就是我专门开发的库,底层用的 map ,有些API也贴近 map,API如下:
初始化参数
| 参数 | 默认值 | 描述 | 版本 |
|---|---|---|---|
size?: number | 100 | 最多缓存多少个 | |
expiration?: number | Number.MAX_SAFE_INTEGER | 按时间毫秒设置缓存有效期,超出时间会被删除 | |
change?: (data: [string, any][]) => void | - | 当缓存变更的时候,可以在此方法内同步外部数据 | 新增于 v0.0.7 |
api
| 名称 | 参数 | 返回值类型 | 描述 | 版本 |
|---|---|---|---|---|
initCache | data: [string, any][] | - | 初始化缓存数据 | 新增于 v0.0.7 |
hasCache | key: string | boolean | 验证是否在缓存中 | |
setCache | key: string, data: any, expiration?: number | - | 设置缓存,expiration 以毫秒为单位设置缓存有效期,优先级高于初始化的 expiration 参数,未设置时默认为 初始化的 expiration | expiration 新增于 v0.0.3 |
getCache | key: string | any | 获取缓存 | |
deleteCache | key: string | - | 删除缓存 | |
deleteCacheByStarts | url: string | - | 根据键值的前缀删除缓存 | |
clearCache | - | - | 清空缓存 | |
cacheSize | - | number | 有多少个缓存 | |
getNowCache | - | any | 获取当前缓存,默认为最后一个,getPreviousCache/getNextCache/goPostionCache/goAbsPostionCache都会影响当前缓存的值 | |
getPreviousCache | - | any | 按设置顺序前一个缓存 | |
getNextCache | - | any | 按设置顺序后一个缓存 | |
goPostionCache | num: number | any | 相对当前缓存获取缓存,1为后一个,-1为前一个 | |
goAbsPostionCache | num: number | any | 按照设置顺序获取第 num 个缓存 | |
getCacheToArray | needTime: boolean = false | [string, any][] | 按设置顺序转换为数组,如果参数为 false,则直接返回设置的数据,如果为 true,则会返回 { dateTime: 过期时间, data: 设置数据 } | dateTime 参数新增于 v0.0.7 |
同步缓存内外数据
当我们希望缓存内的数据和缓存外联动时,我们可以初始化时传入 第三个参数 change 函数, change 函数的参数就是缓存内的数据(内部数据的结构是 { dateTime: 过期时间, data: 设置的缓存 }),所以如果与 localStorage 联动如下:
const localCache = new CacheMemory(100, 100000, (data) => {localStorage.setItem('localCache', JSON.stringify(data))
})
localCache.setCache('aaa', 111)
localCache.setCache('bbb', 222)
那下次再打开浏览器,localStorage 内的值如何传递到缓存中,此时可以初始化之后使用 initCache :
const initCache = new CacheMemory()
const localStorageCache = localStorage.getItem('localCache')
if (localStorageCache) {initCache.initCache(JSON.parse(localStorageCache))
}
console.log(initCache.getCacheToArray())
Vue:
const cacheList = ref<[string, any][]>([])
const localCache = new CacheMemory(100, 100000, (data) => {cacheList.value = data
})
React:
const [cacheList, setCacheList] = useState<[string, any][]>([])
const localCache = new CacheMemory(100, 100000, (data) => {setCacheList(data)
})
实际上面还有个问题,就是添加缓存之后,什么时候使缓存失效,虽然有过期时间一说,但设的小了,缓存没效果,设得大了,就涉及需要清缓存。
这里我提供几个思路:
- 过期时间设的小一点,仅保证多个组件同时加载接口时做到缓存;
- 接口映射表,哪些接口改变之后需要清缓存,做好映射关系,在
interceptors.response中清除对应缓存,这样项目中的代码不用动; - 如果项目完全采用的 REST API 风格,可以在
post/put/delete中清除对应缓存,此处有个 例子。
最后我们采用了第一种思路,解决服务器临时压力,因为接口规范不统一,接口映射表又太多,一时难以保证齐全。
完整例子可以查看 vue-components ,本地运行,接口 mock 。
相关文章:
为了实现接口缓存,专门写了个缓存库 f-cache-memory
问题起因 起因是某次发版之后,服务器接口压力过大,当场宕机,排查之后发现有个接口在首页被调十来次(六七年的老项目了,都是泪呀),后端反馈这个接口的sql很复杂,很耗性能,…...
actual combat 35 —— es
一、windows中es执行步骤 参考:https://blog.csdn.net/qq_21197507/article/details/115076913 下es安装包下es前端gitHub代码,然后npm -i安装,npm run start 启动安装kibana 二、遇到的问题 1. 第二步安装前端代码依赖报错 npm ERR! co…...
android R ext4 image打包脚本介绍
一、Android R打包指令使用介绍 (1)mkuserimg_mke2fs #./mkuserimg_mke2fs --help usage: mkuserimg_mke2fs [-h] [--android_sparse] [--journal_size JOURNAL_SIZE][--timestamp TIMESTAMP] [--fs_config FS_CONFIG][--product_out PRODUCT_OUT][--b…...
美式键盘 QWERTY 布局的来历
注:机翻,未校对。 The QWERTY Keyboard Is Tech’s Biggest Unsolved Mystery QWERTY 键盘是科技界最大的未解之谜 It’s on your computer keyboard and your smartphone screen: QWERTY, the first six letters of the top row of the standard keybo…...
ETL数据同步之DataX,附赠一套DataX通用模板
今天跟大家分享数据同步datax的模板,小伙伴们简单直接借鉴使用。 还记得上一篇关于大数据DS调度工具的分享嘛? 主流大数据调度工具DolphinScheduler之数据ETL流程-CSDN博客 里面的核心就是采用了DATAX的数据同步原理。 一,什么是DataX D…...
[论文笔记] CT数据配比方法论——1、Motivation
我正在写这方面的论文,感兴趣的可以和我一起讨论!!!!!! Motivation 1、探测原有模型的配比: 配比 与 ppl, loss, bpw, benchmark等指标 之间的关系。 2、效果稳定的配比:配比 与 模型效果 之间的规律。 Experiments 1、主语言(什么语言作为主语言,几种主语言?…...
某4G区域终端有时驻留弱信号小区分析
这些区域其实是长时间处于连接态的电信卡4G终端更容易出现。 出现问题时都是band1 100频点下发了针对弱信号的1650频点的连接态A4测量事件配置(其阈值为-106)。而这个条件很容易满足,一旦下发就会切到band3 1650频点。 而1650频点虽然下发ban…...
【体外诊断】ARM/X86+FPGA嵌入式计算机在免疫分析设备中的应用
体外诊断 信迈提供基于Intel平台、AMD平台、NXP平台的核心板、2.5寸主板、Mini-ITX主板、4寸主板、PICO-ITX主板,以及嵌入式准系统等计算机硬件。产品支持GAHDMI等独立双显,提供丰富串口、USB、GPIO、PCIe扩展接口等I/O接口,扩展性强…...
Linux上启动和停止jar
linux 后台运行jar 在Linux系统中,要想让jar包在后台运行,可以使用nohup命令和&符号。nohup命令可以使进程在后台不受挂起信号影响的执行,而&符号则是将任务放入后台执行。 以下是一个简单的命令示例,它将启动一个jar包…...
浏览器缓存:强缓存与协商缓存实现原理有哪些?
1、强缓存:设置缓存时间的,那么在这个时间内浏览器向服务器发送请求更新数据,但是服务器会让其从缓存中获取数据。 可参考:彻底弄懂强缓存与协商缓存 - 简书 2、协商缓存每次都会向浏览器询问,那么是怎么询问的呢&…...
持续集成04--Jenkins结合Gitee创建项目
前言 在持续集成/持续部署(CI/CD)的旅途中,Jenkins与版本控制系统的紧密集成是不可或缺的一环。本篇“持续集成03--Jenkins结合Gitee创建项目”将引导如何将Jenkins与Gitee(一个流行的Git代码托管平台)相结合ÿ…...
【Node.js基础02】fs、path模块
目录 一:fs模块-读写文件 1 加载fs模块对象 2 读制定文件内容文件 3 向文件中写入内容 二:path模块-路径处理 1 问题引入 2 __dirname内置变量 使用方法 一:fs模块-读写文件 fs模块封装了与本机文件系统交互方法和属性 1 加载fs模块…...
牛客TOP101:单链表的排序
文章目录 1. 题目描述2. 解题思路3. 代码实现 1. 题目描述 2. 解题思路 按我们以往的排序算法来看,针对链表来说都是太不合适,因为很多都会出现指针前移后移,后移还好说,前移对于链表来说就太难了,而且大部分都是某一个…...
数据可视化配色新工具,颜色盘多达2500+类
好看的配色,不仅能让图表突出主要信息,更能吸引读者,之前分享过很多配色工具,例如, 👉可视化配色工具:颜色盘多达3000+类,数万种颜色! 本次再分享一个配色工具pypalettes,颜色盘多达2500+类。 安装pypalettes pip install pypalettes pypalettes使用 第1步,挑选…...
SpringAI简单使用(本地模型+自定义知识库)
Ollama 简介 Ollama是一个开源的大型语言模型服务工具,它允许用户在本地机器上构建和运行语言模型,提供了一个简单易用的API来创建、运行和管理模型,同时还提供了丰富的预构建模型库,这些模型可以轻松地应用在多种应用场景中。O…...
为什么要从C语言开始编程
在开始前刚好我有一些资料,是我根据网友给的问题精心整理了一份「C语言的资料从专业入门到高级教程」, 点个关注在评论区回复“888”之后私信回复“888”,全部无偿共享给大家!!!很多小伙伴在入门编程时。都…...
[数据集][目标检测]导盲犬拐杖检测数据集VOC+YOLO格式4635张2类别
数据集格式:Pascal VOC格式YOLO格式(不包含分割路径的txt文件,仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数):4635 标注数量(xml文件个数):4635 标注数量(txt文件个数):4635 标注…...
数据结构(稀疏数组)
简介 稀疏数组是一种数据结构,用于有效地存储和处理那些大多数元素都是零或者重复值的数组。在稀疏数组中,只有非零或非重复的元素会被存储,从而节省内存空间。 案例引入 假如想把下面这张表存入文件,我们会怎么做?…...
python 爬虫技术 第02节 基础复习
Python基础复习 Python 是一种高级、通用、解释型的编程语言,以其简洁的语法和强大的功能在数据科学、Web 开发、自动化脚本编写、机器学习等领域广泛使用。下面是一些 Python 基础概念的复习: 1. 数据类型 Python 支持多种内置数据类型,包…...
数据结构-C语言-排序(3)
代码位置:test-c-2024: 对C语言习题代码的练习 (gitee.com) 一、前言: 1.1-排序定义: 排序就是将一组杂乱无章的数据按照一定的规律(升序或降序)组织起来。(注:我们这里的排序采用的都为升序) 1.2-排序分…...
调用支付宝接口响应40004 SYSTEM_ERROR问题排查
在对接支付宝API的时候,遇到了一些问题,记录一下排查过程。 Body:{"datadigital_fincloud_generalsaas_face_certify_initialize_response":{"msg":"Business Failed","code":"40004","sub_msg…...
DeepSeek 赋能智慧能源:微电网优化调度的智能革新路径
目录 一、智慧能源微电网优化调度概述1.1 智慧能源微电网概念1.2 优化调度的重要性1.3 目前面临的挑战 二、DeepSeek 技术探秘2.1 DeepSeek 技术原理2.2 DeepSeek 独特优势2.3 DeepSeek 在 AI 领域地位 三、DeepSeek 在微电网优化调度中的应用剖析3.1 数据处理与分析3.2 预测与…...
【人工智能】神经网络的优化器optimizer(二):Adagrad自适应学习率优化器
一.自适应梯度算法Adagrad概述 Adagrad(Adaptive Gradient Algorithm)是一种自适应学习率的优化算法,由Duchi等人在2011年提出。其核心思想是针对不同参数自动调整学习率,适合处理稀疏数据和不同参数梯度差异较大的场景。Adagrad通…...
为什么需要建设工程项目管理?工程项目管理有哪些亮点功能?
在建筑行业,项目管理的重要性不言而喻。随着工程规模的扩大、技术复杂度的提升,传统的管理模式已经难以满足现代工程的需求。过去,许多企业依赖手工记录、口头沟通和分散的信息管理,导致效率低下、成本失控、风险频发。例如&#…...
连锁超市冷库节能解决方案:如何实现超市降本增效
在连锁超市冷库运营中,高能耗、设备损耗快、人工管理低效等问题长期困扰企业。御控冷库节能解决方案通过智能控制化霜、按需化霜、实时监控、故障诊断、自动预警、远程控制开关六大核心技术,实现年省电费15%-60%,且不改动原有装备、安装快捷、…...
STM32标准库-DMA直接存储器存取
文章目录 一、DMA1.1简介1.2存储器映像1.3DMA框图1.4DMA基本结构1.5DMA请求1.6数据宽度与对齐1.7数据转运DMA1.8ADC扫描模式DMA 二、数据转运DMA2.1接线图2.2代码2.3相关API 一、DMA 1.1简介 DMA(Direct Memory Access)直接存储器存取 DMA可以提供外设…...
NLP学习路线图(二十三):长短期记忆网络(LSTM)
在自然语言处理(NLP)领域,我们时刻面临着处理序列数据的核心挑战。无论是理解句子的结构、分析文本的情感,还是实现语言的翻译,都需要模型能够捕捉词语之间依时序产生的复杂依赖关系。传统的神经网络结构在处理这种序列依赖时显得力不从心,而循环神经网络(RNN) 曾被视为…...
IoT/HCIP实验-3/LiteOS操作系统内核实验(任务、内存、信号量、CMSIS..)
文章目录 概述HelloWorld 工程C/C配置编译器主配置Makefile脚本烧录器主配置运行结果程序调用栈 任务管理实验实验结果osal 系统适配层osal_task_create 其他实验实验源码内存管理实验互斥锁实验信号量实验 CMISIS接口实验还是得JlINKCMSIS 简介LiteOS->CMSIS任务间消息交互…...
10-Oracle 23 ai Vector Search 概述和参数
一、Oracle AI Vector Search 概述 企业和个人都在尝试各种AI,使用客户端或是内部自己搭建集成大模型的终端,加速与大型语言模型(LLM)的结合,同时使用检索增强生成(Retrieval Augmented Generation &#…...
【Go语言基础【12】】指针:声明、取地址、解引用
文章目录 零、概述:指针 vs. 引用(类比其他语言)一、指针基础概念二、指针声明与初始化三、指针操作符1. &:取地址(拿到内存地址)2. *:解引用(拿到值) 四、空指针&am…...
