面试中可能会被问到的http缓存问题

经过几次校招中的面试,发现http缓存这个知识点一直被拿来当成面试题,我第一次遇到这个问题时被问得一脸懵,于是便总结出以下的内容

在任何一个前端项目中,访问服务器获取数据都是很常见的事情,但是如果相同的数据被重复请求了不止一次,那么多余的请求次数必然会浪费网络带宽,以及延迟浏览器渲染所要处理的内容,从而影响用户的使用体验。如果用户使用的是按量计费的方式访问网络,那么多余的请求还会隐性地增加用户的网络流量资费。因此考虑使用缓存技术对已获取的资源进行重用,是一种提升网站性能与用户体验的有效策略。

缓存的原理是在首次请求后保存一份请求资源的响应副本,当用户再次发起相同请求后,如果判断缓存命中则拦截请求,将之前存储的响应副本返回给用户,从而避免重新向服务器发起资源请求。

缓存的技术种类有很多,比如代理缓存、浏览器缓存、网关缓存、负载均衡器及内容分发网络等,它们大致可以分为两类:共享缓存和私有缓存。共享缓存指的是缓存内容可被多个用户使用,如公司内部架设的Web代理;私有缓存指的是只能单独被用户使用的缓存,如浏览器缓存。

HTTP 缓存应该算是前端开发中最常接触的缓存机制之一,它又可细分为强制缓存与协商缓存,二者最大的区别在于判断缓存命中时,浏览器是否需要向服务器端进行询问以协商缓存的相关信息,进而判断是否需要就响应内容进行重新请求。下面就来具体看HTTP缓存的具体机制及缓存的决策策略。

强缓存

// 响应头通过设置Expires
res.writeHead(200, {
    
          // 缺点:客户端时间和服务器时间可能不同步      
    Expires: new Date('2021-5-27 21:40').toUTCString()    
})
// 通过设置Cache-Control
res.writeHead(200, {
    
          
    'Cache-Control': 'max-age=5' // 滑动时间,单位是秒   
})

协商缓存

// 通过设置文件修改时间来进行协商缓存判断
const {
    
     mtime } = fs.statSync('./img/03.jpg')     
const ifModifiedSince = req.headers['if-modified-since']     
if (ifModifiedSince === mtime.toUTCString()) {
    
          // 缓存生效      
    res.statusCode = 304      
    res.end()      
    return   
}     
const data = fs.readFileSync('./img/03.jpg')     
// 告诉客户端该资源要使用协商缓存    
//   客户端使用缓存数据之前问一下服务器缓存有效吗    
//   服务端:    
//     有效:返回 304 ,客户端使用本地缓存资源    
//     无效:直接返回新的资源数据,客户端直接使用    
res.setHeader('Cache-Control', 'no-cache')    
// 服务端要下发一个字段告诉客户端这个资源的更新时间    
res.setHeader('last-modified', mtime.toUTCString())    
res.end(data)
// last-modified的不足
// 见下面


// 通过文件内容是否变化进行协商缓存判断
const data = fs.readFileSync('./img/04.jpg')    
// 基于文件内容生成一个唯一的密码戳    
const etagContent = etag(data)     
const ifNoneMatch = req.headers['if-none-match']     
if (ifNoneMatch === etagContent) {
    
          
    res.statusCode = 304      
    res.end()      
    return    
}     
// 告诉客户端要进行协商缓存    
res.setHeader('Cache-Control', 'no-cache')    
// 把该资源的内容密码戳发给客户端    
res.setHeader('etag', etagContent)    
res.end(data)

通过 last-modified 所实现的协商缓存能够满足大部分的使用场景,但也存在两个比较明显的缺陷:

  • 首先它只是根据资源最后的修改时间戳进行判断的,虽然请求的文件资源进行了编辑,但内容并没有发生任何变化,时间戳也会更新,从而导致协商缓存时关于有效性的判断验证为失效,需要重新进行完整的资源请求。这无疑会造成网络带宽资源的浪费,以及延长用户获取到目标资源的时间。
  • 其次标识文件资源修改的时间戳单位是秒,如果文件修改的速度非常快,假设在几百毫秒内完成,那么上述通过时间戳的方式来验证缓存的有效性,是无法识别出该次文件资源的更新的。

其实造成上述两种缺陷的原因相同,就是服务器无法仅依据资源修改的时间戳来识别出真正的更新,进而导致重新发起了请求,该重新请求却使用了缓存的 Bug 场景。

基于ETag的协商缓存

为了弥补通过时间戳判断的不足,从 HTTP 1.1 规范开始新增了一个 ETag 的头信息,即实体标签(Entity Tag)。

其内容主要是服务器为不同资源进行哈希运算所生成的一个字符串,该字符串类似于文件指纹,只要文件内容编码存在差异,对应的 ETag 标签值就会不同,因此可以使用 ETag 对文件资源进行更精准的变化感知。

缓存决策树

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/qq_45503196/article/details/121708797
今日推荐