内功修炼之“HTTP缓存机制详解”,每天多会一点,薪资多涨一点~

480 阅读5分钟

前言

身为Web开发人员,针对http问题一定是面试中的常见考点,今天给大家介绍下,http的缓存机制。如果对你有所帮助,可以点个小赞咯~

直奔主题吧,先来看看什么是缓存:

缓存

目的:存储将被用到的数据,让数据访问更快。

缓存中名词概念:
·命中:在缓存中找到了请求的数据;
·未命中(穿透):缓存中没有需要的数据
·命中率:命中次数/总次数 *100%
·缓存大小:缓存中一共可以存多少数据
·清空策略:如果缓存空间不够数据如何被替换

清空策略(3种)

1.FIFO(First in first out)

众所周知的"先进先出",例如最多缓存100条数据,那么当101条数据需要添加时,会删除掉最开始添加的第1条数据。如下图:

2.LFU(Less Frequently Used)

策略如其名,当缓存控件不够时新的数据会替换掉命中率最低的那条缓存数据。

  1. LRU(Least Recently Used)

最近最少使用的,缓存的元素有一个时间戳,当缓存容量满了,而又需要腾出地方来缓存新的元素的时候,那么现有缓存元素中时间戳离当前时间最远的元素将被清出缓存。 (它与FIFO不同的地方在于,前者是命中时间间隔最远的,后者是第一次存储时间间隔最远的)

利用缓存优化项目实例(干货!)

1.FIFO

1.需求:实现一个斐波那契数列中输入位数得到对应项的值;// 1,1,2,3,5,8,13,21,33...
1.1 非缓存版

function _getFib(n) {
    if (n == 1 || n == 2) {
        return 1
    }
    return _getFib(n - 1) + _getFib(n - 2);
}

console.log(_getFib(42)); //267914296 耗时:1.297s
console.log(_getFib(44));//701408733 耗时:5.928s 
console.log(_getFib(43)); //1836311903 耗时:14.979s // 位数越多,计算耗时呈指数形增长,这显然浪费了性能
//因为此函数求值过程中存在分叉,在没有缓存机制的协助下,输入位数每增加1位,计算所需复杂度都要*2,
//等于总计算量为2^n,复杂度呈指数增长。

//所以我们急需一种缓存方法来优化我们计算斐波拉契数列对应项的值,这里我们选用FIFO方式优化:

//1,1,2,3,5,8,13,21,34...

function setMemory(fn,maxLenth = 10) { //缓存函数结合函数式编程,提升可扩展性
    const cache = [];

        return (...args) => {
        const hash = args.join(',');
        let result = cache.find((item) => {
            return item.hash == hash
        })
        if (result){
            return result.value
        }else {
            result = {
                hash:hash,
                value:fn(...args)
            }
             cache.push(result);
            if (cache.length>maxLenth){
                cache.shift();
            }
            return result.value
        }
    }
}
function _getFib(n) {
    if (n == 1 || n == 2) {
        return 1
    }
    return getFib(n - 1) + getFib(n - 2);
}

let getFib = setMemory(_getFib,20);

console.log(getFib(46)); //1836311903 耗时:0.395s
console.log(getFib(56)); //225851433717 耗时:0.398s  差距已经微乎其微,缓存的优化效果相当棒~
   

http缓存策略

Web 缓存大致可以分为:数据库缓存、服务器端缓存(代理服务器缓存、CDN 缓存)、浏览器缓存。 浏览器缓存也包含很多内容: HTTP 缓存、indexDB、cookie、localstorage 等等。这里我们只讨论 HTTP 缓存相关内容。

先介绍下http缓存的一些属性:

cache-control

定义了所有缓存都要遵守的行为。先介绍一些要用到的缓存属性:

可缓存性:

 值         含义
 public	    允许所有方缓存
 private    值允许浏览器缓存
 no-cache   每次必须先询问服务器资源是否已更新
 no-store   不使用缓存

缓存期限

 值         含义
 max-age    秒(存储周期)
 s-maxage   秒(共享缓存如代理等存储周期)

缓存主要分类

1.强制缓存

强制使用缓存,不去服务器对比是否更新;(命中则不再发送请求)

形式:

1.Cache-Contorl: max-age=600

2.Expires: <最后期限>

举例:
const express = require('express');
const app = express();
app.get('/s',(req,res)=>{
    res.set('Cache-Control','max-age=60') //设置强制缓存 存储周期为60S
    res.send("123")
})
app.listen(3000);

生效后如图所示,响应头和状态码处均可看出已修改为强制缓存模式,此时改变服务端代码,重新访问时其返回结果也不会变,因为直接从缓存里读取数据了。

实际应用场景: 强制缓存一般用于缓存某些固定版本的资源,例如:jquery1.9.3.min.js诸如此类内容不会轻易变动的文件,当使用新版jquery时,版本号也会随之改变,就不会命中原先缓存了。

2.协商缓存

客户端与服务店协商使用缓存,每次需要向服务器请求对比,缓存生效则不传回body

形式1:eTag(目前通用的协商缓存方式) 不需要额外设置,默认就是这种。 第一次请求时,响应头会返回一个目前的ETag值,用于之后的校验服务端数据是否有改变。如下图所示:

刷新页面后,如下图所示,请求头中会带上一个If-None-Match属性,其值为上一次请求中响应头里的ETag值,这个值会与本次响应头里的ETag进行比较,一样的话则证明无需更新,状态码304 Not Modified

形式2:Last-Modified模式 res: Last-Modified:<目前最后修整时间,如:前天>

req: If-Modified-Since:<上一次请求传回存储的时间:前天>

例子:
const express = require('express');

const app = express();

app.set('etag',false);//先关闭etag协商缓存
app.get('/s',(req,res)=>{
    res.set('Last-Modified','Tue Oct 20 2020 18:03:21 GMT+0800') //设置Last-Modified协商缓存
   res.send("333")
})

app.listen(3000);

设置完协商缓存第一次请求时,服务端的响应头里会添加上Last-Modified信息,在下一次请求时,请求头里会带上这个Last-Modified信息( If-Modified-Since)用以服务端的缓存验证。

下图是第一次请求时响应头携带的Last-Modified信息,此时请求头没有缓存属性

刷新页面后,请求头里会带上第一次请求的Last-Modified信息( If-Modified-Since)用以给服务端校验。

以上就是本章的主要内容:缓存及http的缓存机制。如果对您有所帮助,可以点个小赞咯~ ~