面试官:你知道http的缓存吗

发布于:2024-04-25 ⋅ 阅读:(17) ⋅ 点赞:(0)

前言

对于前端开发工程师来说,了解http缓存是十分有必要的。在日常开发中使用http缓存,也是我们进行性能优化,改善用户体验所必要的手段之一。因此在面试中也是会经常遇到有面试官提出这个问题。那么下面本人将为大家介绍一下我所对于http缓存的理解。

http缓存是什么

HTTP缓存是指在HTTP协议中使用的一种技术,通过在客户端(如浏览器)和服务器之间存储已经获取过的资源副本,以便在将来的请求中重复使用这些资源。

使用http缓存的原因

在项目中,如果我们需要去向后端请求一张图片,但是它在之前就已经向服务器请求过了,那么我们再一次去请求它则会显得有点多余,并且我们都知道这种请求是极其耗时和消耗性能的。因此我们就引入了http缓存。具体原因如下:

  1. 减少网络流量和延迟: 通过在客户端和服务器之间存储已经获取过的资源副本,可以减少网络带宽的使用量和延迟。当资源被缓存在客户端或者中间代理服务器中时,浏览器无需再次向服务器请求相同的资源,而是直接从缓存中获取,从而节省了网络传输时间和带宽消耗。
  2. 提高网站性能和加载速度: 缓存可以加快网页的加载速度,因为浏览器可以直接从本地缓存中获取资源,而无需等待服务器的响应。特别是对于频繁访问的静态资源(如图片、样式表和脚本文件),通过缓存可以显著减少页面加载时间,提升用户体验。
  3. 降低服务器负载: 当大量请求被缓存时,服务器可以减少处理相同资源请求的次数,从而降低服务器的负载。这对于高流量的网站来说尤为重要,可以降低服务器成本并提高承载能力。
  4. 支持离线浏览: 缓存还可以支持离线浏览功能,即使在没有网络连接的情况下,用户仍然可以访问之前缓存的页面内容。这对于移动设备和应用程序来说尤为重要,因为网络连接可能不稳定或者不可用。

需要注意的是,我们的缓存主要是针对html,css,img等静态资源,常规情况下,我们不会去缓存一些动态资源,因为缓存动态资源的话,数据的实时性就不会不太好,所以我们一般都只会去缓存一些不太容易被改变的静态资源。

http缓存的分类

HTTP 缓存主要分为两种:强缓存和协商缓存。

强缓存

强缓存是通过在HTTP响应头中设置特定的字段,告知客户端在一定的有效期内直接从本地缓存中获取资源,而无需再次向服务器发送请求的一种缓存机制。常见的控制字段包括Cache-ControlExpires

Express

以前是通过使用响应头的Expires字段去实现强缓存。Expires是HTTP/1.0中定义的一个响应头字段,用于指定资源的到期时间,即资源在客户端缓存中的有效期。它的值是一个GMT格式的日期时间字符串。比如说将某一资源设置响应头为:Expires:new Date("2024-4-20 23:59:59");

但是随着时代的发展,现在他已经被废弃掉了,因为Expires判断强缓存是否过期的机制是:获取本地时间戳,并对先前拿到的资源文件中的Expires字段的时间做比较。来判断是否需要对服务器发起请求。因此它就会受到客户端时间的影响,而这就有可能会导致出现错误。

也就是说,Expires过度依赖本地时间,如果本地与服务器时间不同步,就会出现资源无法被缓存或者资源永远被缓存的情况。因此现在我们都是用Cache-Control来代替Expres了。

Cache-Control

Cache-Control是HTTP/1.1中最重要的缓存控制字段之一,它可以指定缓存行为的各种指令。它的使用方法也是很简单的,只需要在响应头添加上需要缓存的时间就行了。如:

res.writeHead(200, {
        'Content-Type': mime.lookup(ext),
        'Cache-Control': 'max-age=86400', // 一天
      })

Cache-Control:max-age=N,N就是需要缓存的秒数。从第一次请求资源的时候开始,往后N秒内,资源若再次请求,则直接从缓存中读取,不会往服务器再发送一次请求。

Cache-control中因为max-age后面的值是一个滑动时间,从服务器第一次返回该资源时开始倒计时。所以也就不需要比对客户端和服务端的时间,解决了Expires所存在的巨大漏洞。

它常见的指令有:

  • public:表示响应可以被任何缓存(包括代理服务器)缓存。
  • private:表示响应只能被客户端缓存,而不允许被代理服务器缓存。
  • s-maxage=<seconds>:类似于max-age,但仅适用于代理服务器缓存。
  • no-cache:表示缓存内容需要重新验证,客户端每次都要向服务器发送请求进行确认。
  • no-store:表示不缓存任何内容,每次请求都要向服务器发送请求。

强缓存的缺点

  • 当用户第一次访问时,服务器会返回资源,并在响应头中设置缓存时间,当用户再次访问时,浏览器会根据缓存时间判断是否使用缓存,如果缓存时间未过期,则使用缓存,如果过期则重新请求资源,这样会导致用户第一次访问时,加载时间较长,用户体验不好。
  • 如果后端改了数据,但是名字没有改变,浏览器不会重新请求,导致数据不是最新的。
  • 后端资源修改了,前端无法实时获取最新资源。(通常都在文件名后面携带一串hash值,只要资源被修改,hash值就会更改,也就是文件名被修改了,因次浏览器一定会重新请求)
  • 通过浏览器url地址栏发送的get请求,无法被强缓存,因为浏览器会忽略缓存,直接请求服务器,但是通过页面上的链接、表单提交、ajax请求等方式发送的get请求,可以被强缓存。

协商缓存

协商缓存是 HTTP 缓存的一种机制,它通过在请求和响应中使用一些特定的头部信息来协商客户端和服务器之间的缓存行为。这种缓存机制允许客户端在重新获取资源时向服务器询问该资源的状态,并根据服务器返回的状态来决定是否使用缓存。

在协商缓存中,主要使用的头部字段包括:

  1. Last-Modified / If-Modified-Since:

    • 当服务器响应一个资源请求时,会在响应头中包含 Last-Modified 字段,表示资源的最后修改时间。
    • 当客户端再次请求相同资源时,会在请求头中包含 If-Modified-Since 字段,值为上一次获取资源时服务器返回的 Last-Modified 时间。
    • 服务器收到带有 If-Modified-Since 字段的请求后,会比较该字段的值与资源的最后修改时间,如果资源的最后修改时间晚于 If-Modified-Since 字段的值,则返回资源内容和状态码 200 OK;如果资源的最后修改时间早于或等于 If-Modified-Since 字段的值,则返回状态码 304 Not Modified,告知客户端可以使用缓存的资源。
  2. ETag / If-None-Match:

    • 当服务器响应一个资源请求时,可以在响应头中包含 ETag 字段,表示资源的标识符。
    • 当客户端再次请求相同资源时,可以在请求头中包含 If-None-Match 字段,值为上一次获取资源时服务器返回的 ETag
    • 服务器收到带有 If-None-Match 字段的请求后,会比较该字段的值与资源的当前 ETag,如果两者相等,则返回状态码 304 Not Modified,告知客户端可以使用缓存的资源;如果两者不相等,则返回新的资源内容和状态码 200 OK

就比如:

 res.writeHead(200, {
        'Content-Type': mime.lookup(ext),
        'etag': `${content}`
        // 'Last-Modified': stats.mtimeMs //时间戳
      })
const content = fs.readFileSync(filePath) // 读取文件
      const { ext } = path.parse(filePath);
      const timeStamp = req.headers['if-modified-since'];
      let status = 200;
      if (timeStamp && Number(timeStamp) === stats.mtimeMs) {
        status = 304;
      }

通过以上操作,我们就可以实现一个简单的协商缓存了。需要注意的是,之所以会有ETag / If-None-Match,是因为:

  • Last-Modified标注的最后修改只能精确到秒级,如果某些文件在1秒钟以内,被修改多次的话,它将不能准确标注文件的修改时间
  • 如果某些文件被修改了,但是内容并没有任何变化,而Last-Modified却改变了,导致文件没法使用缓存
  • 有可能存在服务器没有准确获取文件修改时间,或者与代理服务器时间不一致等情形

总结

HTTP缓存是通过在客户端和服务器之间存储已获取过的资源副本来提高网站性能和加载速度的一种技术。它分为强缓存和协商缓存两种类型。

强缓存通过设置特定的响应头字段(如Cache-ControlExpires),告知客户端在一定的有效期内直接从本地缓存中获取资源,而无需再次向服务器发送请求。强缓存可以减少网络流量和延迟,提高网站性能和加载速度,降低服务器负载,并支持离线浏览。常见的控制字段有ublic、private、s-maxage、no-cache和no-store

协商缓存则是通过在请求和响应中使用特定的头部信息(如Last-Modified / If-Modified-SinceETag / If-None-Match)来协商客户端和服务器之间的缓存行为。当客户端再次请求资源时,会根据服务器返回的状态码来决定是否使用缓存。如果资源未过期,则返回状态码200 OK;如果资源过期,则返回状态码304 Not Modified,告知客户端可以使用缓存的资源。