HTTP缓存解释为何页面响应的数据和服务端的不一致

HTTP缓存解释为何页面响应的数据和服务端的不一致

用途

HTTP缓存主要用在对一些实时性要求不高的静态文件进行的缓存,往往都是存在浏览器端,防止这些“多余”的请求重复的访问服务器,对服务器造成压力,从而提高网站的性能。

原理

现有两端,浏览器和服务器端。

浏览器向服务器发送请求,获取一个文件f

服务器就把f给返回浏览器

假如这个文件的内容变化不是那么快,一两周更新一次,浏览器每次请求服务器都返回相同的文件,岂不是对服务器资源的一种浪费?

如何解决呢?

浏览器把请求后拿到的文件存到本地,等下次请求的时候,看看本地是否有缓存文件,如果有,直接拿本地的文件,岂不是就不用请求服务器了?这其实就是http缓存的最最根本的原理。

浏览器端把请求来的文件缓存到如图下f的小方格内

等到下次浏览器端再次请求此文件时,就直接从浏览器缓存的文件中拿,而不再向服务器端发起请求了

以下浏览器截图中标红的部分,就是没有发起请求,直接从浏览器缓存中获取的数据

两种缓存方式

浏览器端有了缓存之后,不能一直有效吧,如果文件更新了,我们还继续使用浏览器缓存中的数据,虽说时效性不强,但长期使用旧文件也不算合理吧。

http协议提供了两种维度来让缓存失效:时间和文件的修改。

可缓存性

  • public

    客户端和代理服务器都可以缓存,大部分情况可以认为public和private是一样的

  • private

    客户端可以缓存

  • no-cache

    需要使用另外一种http缓存策略来验证缓存数据

到期

  • max-age= <seconds>

    缓存的内容将在 xxx 秒后失效(时间就是在这儿设置的)

  • s-maxage= <seconds>

  • max-stale= <seconds>

重新验证

  • must-revalidate

    验证缓存是否到期了

  • proxy-revalidate

    发送一个报头,告诉浏览器当前页面不进行缓存,每次访问的时间必须从服务器上读取最新的数据

其他

  • no-store

    所有缓存策略都不会进行(这里指的是两种缓存策略都不会进行)

cache-control 缓存原理

test.html

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    <script src="/script.js"></script>
  </body>
</html>

server.js

const http = require("http");
const fs = require("fs");

http
  .createServer(function (request, response) {
    
    
    console.log("request come", request.url);

    if (request.url === "/") {
    
    
      const html = fs.readFileSync("test.html", "utf8");
      response.writeHead(200, {
    
    
        "Content-Type": "text/html",
      });
      response.end(html);
    }

    if (request.url === "/script.js") {
    
    
      response.writeHead(200, {
    
    
        "Content-Type": "text/javascript",
        "Cache-Control": "max-age=60",
      });
      response.end("console.log('script loaded  twice')");
    }
  })
  .listen(8888);

console.log("服务器开启了 - 8888");

第一次访问请求,客户端向服务端发起一个文件请求,服务器返回文件并在response中加了响应头"Cache-Control:max-age=60",这样一来,这个f文件只能在浏览器端存60秒

在这60秒钟,客户端请求服务器的f文件会直接从缓存中拿取

60秒过后,缓存失效,浏览器再次请求文件需要重新向服务器发起请求。

注意:假如说请求中包含“Cache-Control:max-age=0”或者“Cache-Control:no-store”无论响应中返回的"max-age"值是多少,都不会缓存到服务器。浏览器中对于地址栏中直接输入文件地址的请求做了优化处理,加上了“Cache-Control:max-age=0”,也就是说,如果这个css、js或者其他静态文件是通过你在浏览器上直接输入获得的,将会每时每刻都是获取最新的。

通过查看文件的修改来让缓存失效

这种维度比较的科学:浏览器先请求服务获得文件后,服务器会返回该文件的最后修改时间Last-Modified,作为文件的一个标识,下次浏览器请求的时候,会带着这个标识去请求(此时为If-Modified-Since),然后服务器做校验,如果说时间标识If-Modified-Since等于服务器的文件修改时间,则说明没有修改,返回304状态码,浏览器从缓存中获取文件,但是如果浏览器保存的时间标识If-Modified-Since小于服务器端的文件修改时间,那么,说明文件发生了修改,浏览器就会重新获取新的文件。
If-Modified-Since的时间如果大于服务器端文件的时间,会被认为是错误的请求)

如图,浏览器C向服务器发S起请求,服务器返回文件的同时还会返回文件的最后修改时间Last-Modified作为文件时间标识,浏览器会将文件和文件时间标识都缓存起来。

假如服务器端的文件f并没有被修改,服务器通过判断请求头带的时间标识If-Modified-Since得出结论后,都会返回状态码304告诉浏览器文件没有被修改,让浏览器使用缓存。

假如服务器端的文件f修改了,那么,浏览器将重新获取文件,并缓存到浏览器中。

虽然通过文件最后修改时间作为标识已经很完美了,但是,还是可能存在一个问题:就是有可能服务器端的文件修改后,又改回原来的样子,这样,虽然文件最后修改时间变了,但是,文件内容并没有改变。这样还是会有多余的请求到达服务器,该如何处理呢?
可以将文件内容作为一个唯一标识,例如可以对文件内容取MD5值作为字段(etag)也传给浏览器端,假如这个文件内容没变化,那么MD5值也不会改变。那么,处理流程就变成了这样:服务器端先判断文件修改时间是否发生了变化,如果发生了变化,那么再对比浏览器传来的If-None-Match即浏览器端保留的E-tag值,如果发生了变化,则证明文件修改了,需要浏览器重新下载文件,如果没有,则证明文件内容没变化,返回304状态码。

如图,浏览器要访问服务器的f文件,服务器S返回了文件最后修改时间Last-Modified和文件的内容标识E-tag,浏览器将这两个字段及其文件缓存了起来

当文件最后修改时间没变,文件内容也没变的时候,返回304,让浏览器从缓存中拿取文件。

当文件最后修改时间变了,文件内容没变的时候,返回304,让浏览器从缓存中拿取文件。

当文件修改时间变了,文件内容也变了的时候,服务器会重新下发新的文件给浏览器。

此维度让缓存失效牵扯的http字段有点多,我们最后整理一下:
文件最后修改时间字段:

  • Response:Last-Modified
  • Request:If-Modified-Since

文件内容标识字段:

  • Response:E-tag
  • Request:If-None-Match

众里寻他千百度,慕然回首,点关注

写文章不易,求三连~/

猜你喜欢

转载自blog.csdn.net/Cool_breeze_/article/details/108058138
今日推荐