肉烂在锅里

个人站

我是软件研发中心培训主管,我喜欢看动漫,学习web前端开发。


缓存

之前我们提到了很多关于浏览器存储的缓存策略,在很多业务场景中,尤其是大规模系统中,我们是希望资源可以被自动缓存,而不是我们通过localStorage等方式手动进行缓存。

那么这种情况下,我们就需要浏览器端和服务器端共同协商一种方式来实现对于大规模数据缓存的情况。

为了看清楚我们要研究的问题到底是什么,我们先来看看实际应用场景中的一些例子:

这是手淘中Network中截取的一些信息,可以看到,有一些文件的状态吗是304,有些文件的状态码是200,这些被圈住的状态码为200的文件的Size处显示’from memory cache’,还有的显示为’from disk cache’,同时,他们的下载速度极快,消耗时间甚至小于1ms。

再来看一下这些被圈住的文件的详细的缓存信息是如何的:

就是因为这三个文件在chrome里的缓存信息不同,才导致了他们的缓存策略的不同。

那是什么让这三个文件的缓存策略不同的呢? 手淘网站上为什么会出现这样的缓存策略呢? 这也正是我们要研究的内容。

在我们开始探讨之前,应该知道一点:

浏览器与服务器对文件的缓存策略的协商是借助httpheader上某些属性的配置做到的。

Cache-control所控制的缓存策略

Cache-control就是httpheader中配置缓存策略的一个关键属性,他可以出现在responseHeader中也可以出现在requestHeader中。主要包含以下属性:

  1. max-age

  2. S-maxage

(3)Private

(4)Public

(5)no-cache

(6)no-store

上面这张图片中的信息是来自京东官网中一张图片的请求信息,从图中可以看到,cache-controll存在一个max-age属性,后面跟着一个最大时间数,也就是说,这张图片在第一次从服务端请求后的315360000ms内,是不会再次向服务端发起请求的,因为这相当于服务器告诉客户端,这张图片在这些时间内都是有效的,是不会过期的。这样一来客户端再次需要这张图片的时候就不会向服务端发起请求,直接从缓存中读取。 另外它的状态码是200,并且也有标注 from memory cache。

TIP:有可能会注意到下面还有一个expires属性,他也设置着过期时间,但是在有max-age存在的时候,他是无效的,因为max-age的优先级更高。

再看另一种情况,这是一个标有304状态码的缓存资源。

我们可以注意到,这里不仅仅设置了max-age,还设置了s-maxage ,之后它的状态码就不再是200了,而是304。那这是我为什么呢?

首先,s-maxage和maxage的意思是一样的,都是指定缓存时间,只不过s-maxage指定的缓存时间是只能针对public类的缓存设备而言的,并且s-maxage的优先级高于max-age。

缓存设备有很多种,不仅仅我们的浏览器可以作为缓存设备,我们的CDN也可以作为缓存设备。在浏览器和服务器之间还可能存在着代理服务器,他也可以作为我们的缓存设备。

缓存设备的类型分为:public和Private。private的缓存设备就是私人的缓存设备,比如用户在浏览器中所缓存的信息,只有访问这个浏览器的用户才能使用。对于public的缓存设备,CDN就是一个最好的例子,这种CDN的缓存可以被广大用户所使用。

所以对于上图所描述的信息,这张状态码为304的图片,他的s-maxage就是他在public类型的缓存设备上的缓存过期时间,而不是浏览器缓存的过期时间。因此,他没有从浏览器的缓存中拿取缓存,而是从CDN上拿取的,请求发送给了CDN没有from memory cache,所以状态码是304就很好理解了。

TIP: 304状态码表示文件来自缓存。这通常是经过服务端判断请求的资源内容并没有变化,因此可以直接存缓存中读取。这必然会节省带宽。而200只代表服务端正常相应所需文件,或者说得到了应该得到的文件。按照我的理解,304所表示的情况应该是200所表示的情况的子集。

对于cache-control的第5个属性no-cache,很多人都认为他是不许浏览器去读缓存,其实这样的理解是错误的!

No-cache并非是不许浏览器读取缓存,而是在读取缓存之前无论如何都像服务端发起请求,来从服务端来验证文件是否可以从缓存中读取,浏览器和服务器会根据一些标志位来判断文件的是否需要更新。如果判定文件没有发生更新,那么还会从浏览器缓存中读取。

No-cache往往和max-age搭配使用,他和max-age的区别就是在于,他没有缓存失效时间的这一概念,只要文件需要请求就请求,然后进一步从服务端判断文件是否需要重新加载。

那有没有什么方式是让资源丝毫不借助缓存的呢? 答案是有的,就是之前我们误解的no-cache的真身,no-store,带有no-store属性的文件,就不会采取任何缓存策略。

问题:对于返回304的情况并不是很了解,为什么挂载CDN上的资源可以通过设置s-maxage的方式读取缓存,如果是通过先像CDN发送请求,然后在比对和CDN文件是否相同那为什么不选择no-cache?

答:详见CDN的回源机制:

https://www.jianshu.com/p/e7751ecb6f21

可以看看他的参考文章。

暂时肤浅的理解为,CDN在接收到了客户端的请求后,一看过期时间未到,就直接把原来给过的文件再给一份。

last-modified和etag以及整个服务端浏览器端的缓存策略

  1. Expires

Expires之前有提到过,他也是用来设置缓存的有效时间的,他的优先级小于cache-control中的max-age。在Expires设置的时间内,浏览器都会直接从缓存中请求数据,而不会再次发送http请求去请求数据。在第一次请求数据时,服务端返回response Header中会携带expires,浏览器接收到文件后存到缓存中,下次需要这个文件的时候,会先从缓存中查看这个文件,看看expires到没到期,如果没到期,就直接读取,无需再次请求,如果到期则重新向服务端进行请求。

从上图的信息可见,只要当前时间没超过2018年7月24日,缓存中的这个数据就没有过期。浏览器就继续可以从缓存中读取这个数据。

TIP:为什么expires的优先级低于max-age? expires是http1.0的概念,在http2.0中,缓存的管理都希望集中在同一个头中,因此诞生了cache-control这一概念,max-age的优先级高于expires也就理所当然了。

再次强调的是,通过设置Expires读取缓存的方式并不会在和服务端沟通,他会直接从缓存中读取文件。但这样也就会存在一个问题,如果expires时间没到,但是服务端的文件却更新了,那这样浏览器也不知道这个文件到底有没有得到及时的更新,浏览器只知道时间没到,就从缓存读取,浏览器无法感知到服务端的数据到底有没有变化。这样的缓存结果显然不是我们所希望的。因此,也就引出了last-modified的概念

  1. last-modified和if-modefied-since

当浏览器第一次请求数据的时候,服务端会向responseHeader中加入一个叫做last-modified的状态行。Last-modified是一个类似于expires的时间点,这个时间点是这个文件的最后更新时间。浏览器在接收到数据后还是先将文件存在缓存中,只不过在下次请求资源的时候会在requestHeader中加入一个if-modefied-since的请求头(注意在这里发送了http请求给服务端),这个请求头和服务端最近返回的last-modified是相同的。当服务端发现客户端传来的if-modefied-since和现在最新文件的最近更新时间是相同的,那就会向客户端返回304状态码,表示这个文件是最新的,自上次到目前还没有发生过更新,让客户端回去读缓存就好,服务端就不去拉资源给客户端了,这样浏览器就通过和服务端协商的方式感知到了文件是否发生了更新。但是如果if-modefied-since和last-modifed不同,服务端就会表示这个文件已经被修改,客户端缓存中的文件已经不能使用了,需要重新拉取文件给客户端。

这个过程看上去很完美,但是依旧存在缺陷:

  1. 某些服务端不能获取精确的修改时间

  2. 文件修改时间改了,但是文件却没变

为了解决这些问题,我们就得想办法通过类似md5戳来表示内容的状态。如果内容改变md5戳就会自动改变。这样就能保证是否读取缓存永远受‘改变的状态’决定。

这个问题并非没有标准的解决方法,etag的概念由此诞生。

  1. Etag

正如我们之前谈的,文件如果可以根据一个类似md5戳的东西来表示文件的内容就可以做到“文件改变决定是否缓存”。Etag正式专门用来解决这个问题的。

上面这张图中描述的信息就是斗鱼的一个资源请求信息。我们可以看到,在requestHeader中传入了一个if-None-Match,他的值是一个类似md5戳的字符串。当请求回来的时候,responseHeader中则携带了ETag,他的值和if-None-Match相同,所以,可以判断当前服务端的这个文件并没有发生变化。因此和last-modified类似,返回304,直接告知浏览器读取自己的缓存即可。

TIP: Etag的优先级要比if-modified更高。

分级缓存策略总结

强调说明:s-maxage比max-age的优先级更高,并不是浏览器资源的缓存资源,是public的缓存资源,大多数情况下public设备来自CDN。设置s-maxage,因为不能是直接从浏览器的缓存文件中读取,所以浏览器还是需要向CDN发送http请求的,只不过可以理解成,s-maxage的时间未到,CDN返回他的缓存文件中缓存的文件给浏览器,时间过了,就向中心CDN回源拉取最新文件,返回码为200。

打赏一个呗

取消

感谢您的支持,我会继续努力的!

扫码支持
扫码支持
扫码打赏,你说多少就多少

打开支付宝扫一扫,即可进行扫码打赏哦