业务场景

前一段时间刚做完一个项目,先说一下业务场景,有别于其他的前端项目,这次的项目是直接调用第三方服务的接口,而我们的服务端只做鉴权和透传,第三方为了灵活,把接口拆的很零散,所以这个项目就像扔给你一堆乐高颗粒让你组装成一个机器人。所以可以大概分析一下这个项目在请求接口时的一些特点,然后针对性的做一些优化:

  1. 请求接口多,可能你的一个n个条目的列表本来一个接口搞定现在需要n*10个接口才能拿到完整的数据,有些功能模块可能需要请求成千上万次接口;
  2. 基本都是get请求,只读不写;
  3. 接口调用重复率高,因为接口很细碎,所以可能有些常用的接口需要重复调用;
  4. 接口返回的数据实时性要求不高,第三方的数据不是实时更新的,可能一天或者一周才更新一次,但是第三方要求不能以任何的方式落库。

所以综上分析,前端缓存成了一个可行性较高的优化方案。

解决方案

前端的HTTP请求使用的是Axios,因此可以利用Axios的拦截器进行缓存的管理。梳理一下逻辑:

  1. 创建缓存对象;
  2. 请求发起之前判断该请求是否命中缓存:
    1. 是,直接返回缓存内容;
    2. 否,发起请求,请求成功后将请求结果存入缓存中。

如标题所说,这里的缓存策略我们用的是LRU(Least Recently Used)策略,因为缓存不能无限大,过大的缓存可能会导致浏览器页面性能下降,甚至内存泄漏。LRU会在缓存达到最大承载量后删除最近最少使用的缓存内容,因此不用担心缓存无限增大。那么如何实现LRU缓存策略呢?Github上有现成的轮子,但是为了更深入的学习嘛,我们自己来手动实现一个。

实现LRU

LRU主要有两个功能,存、取。梳理一下逻辑:

  1. 存入:

    1. 如果缓存已满,删除最近最少使用的缓存内容,把当前的缓存存进去,放到最常用的位置;
    2. 否则直接将缓存存入最常用的位置。
  2. 读取:
    1. 如果存在这个缓存,返回缓存内容,同时把该缓存放到最常用的位置;
    2. 如果没有,返回-1。

这里我们可以看到,缓存是有优先级的,我们用什么来标明优先级呢?如果用数组存储可以将不常用的放到数组的头部,将常用的放到尾部。但是鉴于数据的插入效率不高,这里我们使用Map对象来作为容器存储缓存。

代码如下:

class LRUCache {
constructor(capacity) {
if (typeof capacity !== 'number' || capacity < 0) {
throw new TypeError('capacity必须是一个非负数');
}
this.capacity = capacity;
this.cache = new Map();
} get(key) {
if (!this.cache.has(key)) {
return -1;
}
let tmp = this.cache.get(key);
// 将当前的缓存移动到最常用的位置
this.cache.delete(key);
this.cache.set(key, tmp);
return tmp;
} set(key, value) {
if (this.cache.has(key)) {
// 如果缓存存在更新缓存位置
this.cache.delete(key);
} else if (this.cache.size >= this.capacity) {
// 如果缓存容量已满,删除最近最少使用的缓存
this.cache.delete(this.cache.keys().next.val);
}
this.cache.set(key, value);
}
}

结合Axios实现请求缓存

理一下大概的逻辑:每次请求根据请求的方法、url、参数生成一串hash,缓存内容为hash->response,后续请求如果请求方法、url、参数一致,即认为命中缓存。

代码如下:

import axios from 'axios';
import md5 from 'md5';
import LRUCache from './LRU.js'; const cache = new LRUCache(100); const _axios = axios.create(); // 将请求参数排序,防止相同参数生成的hash不同
function sortObject(obj = {}) {
let result = {};
Object.keys(obj)
.sort()
.forEach((key) => {
result[key] = obj[key];
});
} // 根据request method,url,data/params生成cache的标识
function genHashByConfig(config) {
const target = {
method: config.method,
url: config.url,
params: config.method === 'get' ? sortObject(config.params) : null,
data: config.method === 'post' ? sortObject(config.data) : null,
};
return md5(JSON.stringify(target));
} _axios.interceptors.response.use(
function(response) {
// 设置缓存
const hashKey = genHashByConfig(response.config);
cache.set(hashKey, response.data);
return response.data;
},
function(error) {
return Promise.reject(error);
}
); // 将axios请求封装,如果命中缓存就不需要发起http请求,直接返回缓存内容
export default function request({
method,
url,
params = null,
data = null,
...res
}) {
const hashKey = genHashByConfig({ method, url, params, data });
const result = cache.get(hashKey);
if (~result) {
console.log('cache hit');
return Promise.resolve(result);
}
return _axios({ method, url, params, data, ...res });
}

请求的封装:

import request from './axios.js';

export function getApi(params) {
return request({
method: 'get',
url: '/list',
params,
});
} export function postApi(data) {
return request({
method: 'post',
url: '/list',
data,
});
}

这里需要注意的一点是,我将请求方法,url,参数进行了hash操作,为了防止参数的顺序改变而导致hash结果不一致,我在hash操作之前,给参数做了排序处理,实际开发中,参数的类型也不一定就是object,可以根据自己的需求进行改造。

如上改造后,第一次请求后,相同的请求再次触发就不会发送http请求了,而是直接从缓存中获取,真是多快好省~

参考:JS 实现一个 LRU 算法

利用LRU策略实现Axios请求缓存的更多相关文章

  1. nuxt 利用lru-cache 做服务器数据请求缓存

    // 运行与服务端的js // node.js lru-cache import LRU from 'lru-cache' const lruCache = LRU({ // 缓存队列长度 max: ...

  2. Spring-cloud (八) Hystrix 请求缓存的使用

    前言: 最近忙着微服务项目的开发,脱更了半个月多,今天项目的初版已经完成,所以打算继续我们的微服务学习,由于Hystrix这一块东西好多,只好多拆分几篇文章写,对于一般对性能要求不是很高的项目中,可以 ...

  3. 【面试题】LRU算法及编码实现LRU策略缓存

    概念 LRU(least recently used)就是将最近不被访问的数据给淘汰掉,LRU基于一种假设:认为最近使用过的数据将来被使用的概率也大,最近没有被访问的数据将来被使用的概率比较低. 原理 ...

  4. SpringCloud实战-Hystrix线程隔离&请求缓存&请求合并

    接着上一篇的Hystrix进行进一步了解. 当系统用户不断增长时,每个微服务需要承受的并发压力也越来越大,在分布式环境中,通常压力来自对依赖服务的调用,因为亲戚依赖服务的资源需要通过通信来实现,这样的 ...

  5. SpringCloud实战4-Hystrix线程隔离&请求缓存&请求合并

    接着上一篇的Hystrix进行进一步了解. 当系统用户不断增长时,每个微服务需要承受的并发压力也越来越大,在分布式环境中,通常压力来自对依赖服务的调用,因为亲戚依赖服务的资源需要通过通信来实现,这样的 ...

  6. Android Xutils框架HttpUtil Get请求缓存问题

    话说,今天和服务器开发人员小小的逗逼了一下,为啥呢? 话说今天有个"收藏产品"的请求接口,是get request的哦,我客户端写好接口后,点击"收藏按钮",返 ...

  7. 【安卓中的缓存策略系列】安卓缓存策略之磁盘缓存DiskLruCache

    安卓中的缓存包括两种情况即内存缓存与磁盘缓存,其中内存缓存主要是使用LruCache这个类,其中内存缓存我在[安卓中的缓存策略系列]安卓缓存策略之内存缓存LruCache中已经进行过详细讲解,如看官还 ...

  8. Vue之单文件组件的数据传递,axios请求数据及路由router

    1.传递数据 例如,我们希望把父组件的数据传递给子组件. 可以通过props属性来进行传递. 传递数据三个步骤: 步骤1:在父组件中,调用子组件的组名处,使用属性值的方式往下传递数据 <Menu ...

  9. axios [æk'si:əʊs] 及 axios 请求配置

    特征 比Jquery轻量,但处理请求不多的时候,可以使用 基于Promise语法标准 支持nodejs 自动转换JSON数据 用法 get // Make a request for a user w ...

随机推荐

  1. 一个线上 Maven 诡异问题排查过程

    å. 前言 现在的大部分 Java 应用基本都是通过 Maven 进行组织的,不论是分布式应用还是单体集群应用往往都会通过一个 父 POM 加若干子 POM 完成项目的组织.然而这种多应用多模块的拆分 ...

  2. text-decoration属性作用和方法

    text-decoration-line(注释文本添加一条装饰线):none(文本中没有线条). underline(文本的下方显示一条线). overline(文本的上方将显示一条线). line- ...

  3. Local dimming algorithm in matlab

    LED局部背光算法的matlab仿真 最近公司接了华星光电(TCL)的一个项目LCD-BackLight-Local-Diming-Algorithm-IP ,由于没有实际的硬件,只能根据客户给的论文 ...

  4. kubernetes的存活探针和就绪探针

    1.存活探针 使用Kubernetes的一个主要好处是,可以给Kubernetes-个容器列表来由其保持容器在集群中的运行.可以通过让Kubernetes创建pod资源,为其选择一个工作节点并在该节点 ...

  5. 关于使用Draw.io画数据库E-R图的说明

    背景简介 E-R图也称实体-联系图(Entity Relationship Diagram),提供了表示实体类型.属性和联系的方法,用来描述现实世界的概念模型. diagrams.net是用于构建图表 ...

  6. Linux定时任务-cronie

    1.cronie服务介绍 Linux crontab(cronie)是用来定期执行程序的命令. 当安装完成操作系统之后,默认就会启动此任务调度命令. crond 命令每分钟会定期检查是否有要执行的工作 ...

  7. 面试官:spring中定义bean的方法有哪些?我一口气说出了12种,把面试官整懵了。

    前言 在庞大的java体系中,spring有着举足轻重的地位,它给每位开发者带来了极大的便利和惊喜.我们都知道spring是创建和管理bean的工厂,它提供了多种定义bean的方式,能够满足我们日常工 ...

  8. idea debug无法启动 Error running 'Tomcat8': Unable to open debugger port (127.0.0.1:50168): java.net.SocketException "socket closed

    在日志里显示在 event log 里的 Error running 'server_web': Address localhost:1099 is already in use 显示1099单口已被 ...

  9. JDK8安装包的下载安装方式以及环境变量的配置

    前面我们介绍了 <Java是什么?>.<OracleJDK是什么?OracleJDK的版本怎么选择?>.<OpenJDK是什么?>以及<OracleJDK 与 ...

  10. LVS-NAT模式的实现

    一.架构如下: 二.安装过程 1.配置"互联网"服务器 1.1.修改服务器ip为192.168.10.101/24 [root@internet ~]# ip a 1: lo: & ...