前言

要了解缓存首先我们先了解下浏览器的执行机制是怎样的,先看一张图:

  • 浏览器对我们的静态资源,比如说 js css html 图片 字体 等都是有缓存策略的,如果说浏览器自身有缓存,它就会从自己本身的缓存里读了,我们也可以通过网络层类似于我们的服务器包括 Nginx 还有各种各样的静态资源服务器来设置我们的缓存。
  • 如果本机的浏览器有缓存的话,那么他直接就在这个缓存里就获取到了,然后呈现给用户
  • 那么这个缓存还需要查看,如果是在网络上会查看它是否是过期的:
    1. 如果不是过期的,它也是从本地的缓存中读取之后就可以了;
    2. 如果是过期的就需要看一下这个资源有没有 Etag 这样的东西,Etag 是浏览器向服务器发送请求时的一个请求报头,服务器那边也会响应这个请求报头,然后两边共同的匹配一下资源是否过期,如果有 Etag 的话,它就会向浏览器注入 If-None-Match 这样的头。然后向我们的 webserver 发过去,然后服务器那边做一个基础的策略:是否是匹配的 Etag ,如果是匹配的就返回一个 304,如果没有匹配就给 Etag 这个文件一个戳,然后返回 200 更新,这样下次再请求的时候有了这个 Etag 就不会再把整个的资源再重新拉回来了。
    3. 如果没有发现 Etag 的话,在我们服务器还可以设 Expires ,Expires 这个东西就是过期时间,他发现 Last-Modified 这样一个策略,它会向我们的服务器发送一个 If-Modified-Since ,就是说上次的修改时间,然后和服务器的 Expires 做一个对比,如果发现过期了就返回一个新的,如果是没过期就返回一个 304。
    4. 如果是上面的 Etag 和 Last-Modified 两个值都没有,那么他就会直接向我们的 web服务器发送一个请求,然后是请求响应–缓存协商
    5. 如果是上面的 Etag 和 Last-Modified 这两个值都进行设置了,那么就是会将两个值都比较,都满足了之后才会最终得到一个响应,没有谁先谁后的问题,那么当我们设置对应的文件的时候,你会发现很多对应的服务器都是同时的对他们进行设置的,就是设置了 Etag 又设置了 Last-Modified ,所以说当你这个文件有对应的修改的时候,你还要及时的去修改这个 Last-Modified 就是里面的一个 Expires 的东西。
    6. Etag 相比于 Expires 有很多优点,它是一个基于文件的一个戳的比较,可以实现一个实时的更新,Expires 这个东西实际上是做不到那种秒秒级别的,它是一个 Unix 的一个时间戳,他做不到那种特别精细的时间的控制。
    7. 现在的话你也可以在 Header 里面设 control ,可以直接重写掉 Expires 这样的东西。
    8. 如果是一个整个的项目,你不是运维工程师的话,你可以跟运维去说:我需要设置这个文件对应的强缓,就比如说:这个文件 30 年不过期,例如 jquery 这种长时间不过期的文件,这个的话一般就用 Expires 的比较多,就是说过期时间设置较长这个就是强缓,那么 Etag 的话一般是针对的我们的业务的文件。

实践

  • 你需要知道的是:在任何的其他的服务器里面,它们这个的逻辑、思维是一样的,关键是要去看你是怎么去配置的。
  • 操作 Nginx 的基础是你已经在服务器端安装并配置好了,这里以 Centos 为例。

编辑nginx.conf

找到对应的静态资源路径

比如 /usr/share/nginx/ 下的 html 文件夹下 index.html,我们在 body 中加入 jquery 文件

<script src="https://cdn.bootcss.com/jquery/3.2.1/jquery.js"></script>

同时在同级目录下添加 img 文件夹,放入几张图片。

index.html 中添加这些 img 标签引入图片。配置nginx.conf 文件。

在请求所有的目录的路径中匹配 图片 正则表达式,如果匹配的话就是下面的 第一行是 文件的具体路径;第二行是过期时间 30天。

还有一个需要注意的是:再进行验证的时候不能把下图中的这个 Disable cache 勾选上,这个勾选上的话就是禁用缓存了,这里我们实现的是需要缓存的,所以是绝对不能勾选的

来分析下请求到的东西

  • document
    • 请求报文中的 Accept-Encoding 这个可以看到是支持 gzip 压缩的。
    • 返回报文中的 ETag 加了一个戳,这个是 nginx 默认就开启的,如果是想要关闭的话,在 nginx.conf 文件中的 http 对象里加 etag: off; 即可,开启就是 on,不加的话默认的就是 on
    • 可以看到下面请求报文中有一个 If-None-Match 的值也是和上面的 Etag 的值是一样的。
  • jquery
    • 因为这这里默认开启了 Etag 所以当你下次刷新的时候,它会跟服务器做对比,如果 Etag 的值是一样的说明没有改变就返回的是 304 ,可以看到下面请求报文中有一个 If-None-Match 的值也是和上面的 Etag 的值是一样的
  • 再次刷新浏览器,查看 document
    • 这个返回的是 304 是因为我们的 index.html 文件没有改变,ETag 的值也没有发生改变,如果 Etag 值是一样的说明没有改变就返回的是 304 ,可以看到下面请求报文中有一个 If-None-Match 的值也是和上面的 Etag 的值是一样的
  • 开启gzip,同时为了看出效果我们在第二个命令行窗口中修改 index.html 文件之后再刷新浏览器
    • 也会发现 document 返回的报文中多了一个 gzip 的属性,然后是可以看到下面请求报文中有一个 If-None-Match 的值和上面的 Etag 的值已经不一样的,这个的机制是以 If-None-Match 后面的戳去匹配 Etag 也就是服务器响应的戳,发现是不一样的就会重新将更新后的资源拿过来 状态码也是 200
    • 如果是没有这个 Etag ,它会校验 Last-ModifiedIf-Modified-Since 的戳是否一样,服务器来返回相应的状态。

接下来在第一个命令行窗口中在 nginx.conf 文件中增加一个 epirse,过期时间的属性,对比一下效果

  • 这个时候再刷新浏览器查看
    • 这个时候会发现返回报文中多了一个 Expires 过期时间的属性,后面的值刚好是今天再加上 30 天之后的具体日期
  • 还可以在 nginx.conf 文件中加一句话:告诉浏览器不要缓存文件 add_header Cache-Control no-cache;
  • 再刷新浏览器,第一次是 200 第二次是 304 ,还有就是服务器返回时也有明确的标识:Cache-Control: no-cache 就是告诉浏览器不要缓存,然后具体的资源文件是像服务器请求的,服务器来决定返回的状态码

总结

将上面的东西都尝试的设置一遍之后,那么 Nginx 上比较关键的东西:GzipEtagExpires (过期时间)、Cache-Control 这些就都配置好了;

Cache-Control 下面还会有一些非常细致的东西:如果你在 Cache-Control 中配置了更多的过期时间就可以直接把 Expires 直接重写掉了,在一些不支持 HTTP.1 的浏览器里设置 Expires 是没有用的,所以你还是要再设置一个 Cache-Control

百度官网的一个 js 文件的响应头部,有颜色的那些对我们来说都是需要掌握的东西

掌握这个东西之后,对于以后来讲我们就可以跟运维去聊这个东西,比如说:我们的 jquery ,你就可以直接告诉他,我的这个jquery需要在页面上缓存多久多久,这个就可以设置一个 过期时间 Expires 或者是 Cache-Control,我的其他业务文件可能会临时变,所以需要配置 Etag