HTTP 缓存详解

2018-04-29

用途

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

原理

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

这里写图片描述

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

这里写图片描述

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

这里写图片描述

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

如何解决呢?

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

C端浏览器端把请求来的文件缓存到如图下f的小方格内
这里写图片描述

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

这里写图片描述

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

这里写图片描述

两种缓存方式

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

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

利用时间来让缓存失效

时间维度很简单,就是设定一个缓存时间段,过了这个时间段,缓存就自动失效了,浏览器就会发起请求获取文件。这个设定时间的http字段就是cache-control字段。

cache-control 可设置的字段值有:

  • private :客户端可以缓存
  • public :客户端和代理服务器都可缓存,大部分情况可以认为public和private是一样的
  • max-age=xxx : 缓存的内容将在 xxx 秒后失效 (时间就是在这儿设置的)
  • no-cache :需要使用另外一种http缓存策略来验证缓存数据
  • no-store :所有缓存策略都不会进行(这里指的是两种缓存策略都不会进行)

cache-control 缓存原理

第一次访问请求,客户端C向服务端S发起一个文件请求,服务器返回文件并在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起请求,服务器S返回文件的同时还会返回文件的最后修改时间Last-Modified作为文件时间标识,浏览器会将文件和文件时间标识都缓存起来。

这里写图片描述

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

这里写图片描述

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

这里写图片描述

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

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

这里写图片描述

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

这里写图片描述

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

这里写图片描述

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

这里写图片描述

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

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

文件内容标识字段:

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

请关注我

更多精彩内容,请搜索我的微信公众号 码农RyuGou

或者扫码