从需求看HTTP缓存

前端经常会被问HTTP缓存,那么HTTP缓存的发展了解多少呢?为什么会出现各种不同的缓存字段?本篇文章会从缓存的类型开始,逐一说明缓存相关的HTTP字段,相信读完会对以上的问题有所了解。注意,以下讲解只针对HTTP缓存的情况。

缓存分强缓存协商缓存两种,简单来讲的话,强缓存期间请求不会到达服务器,即在缓存有效期内命中缓存,浏览器会使用本地缓存;协商缓存是在没有命中缓存或强缓存失效的情况下,与服务器进行协商来决定返回的请求资源。大概的流程如下:

简略的请求流程

看完上图,从整体上我们了解了HTTP请求的流程,那么这里又引出一些问题,如何知道强缓存的存在?依赖什么条件来确定本地是否有效呢?要解答这些问题,就得知道两个HTTP响应头字段ExpiresCache-Control
HTTP/1.0规范中,响应头字段有一个Expires字段,用于表示当前资源过期的时间。该字段描述的是一个绝对时间,由服务器返回,客户端本地只要超过这个时间则缓存失效。
示例Expires: Sun, 24 May 2020 07:41:19 GMT
Expires是最初的缓存控制字段,为它开始web应用减少了很多不必要的带宽浪费,可以指定一些不常更新的文件形成缓存。但开发者们很快发现了新的问题,Expires依赖客户端的时间进行判断,客户端修改了时间会影响缓存的有效性,这在某些情况下会比较致命,比如一个重要文件,理应在今天过期,但由于客户端时间修改成了去年,造成了缓存没有刷新,这就会很尴尬。为了解决这个问题,在HTTP/1.1中加入了Cache-Control字段来控制缓存,以满足更多的需求,配置如下。需要注意的是,该字段优先级高于Expires

  • public 可以被所有用户缓存,这里包括浏览器、代理服务器、CDN等。
  • private 只能被浏览器缓存,不允许被其它如代理服务器缓存。
  • no-store 不缓存
  • no-cache 先缓存本地,但命中缓存后必须与服务器验证资源新鲜度才能使用。这里注意,不要被名字误导成不缓存了。
  • max-age 用相对时间来表示缓存有效期,表示缓存将在XX秒后失效。示例Cache-Control: max-age=315360000
    Cache-Control设置

如果在缓存有效期内命中缓存,则使用强缓存,但是,如果命中缓存时缓存失效了怎么办?这个时候就要进入协商缓存流程了。进入协商缓存的条件不单是缓存失效,还包含以下三种情况:

  • 第一次请求服务器,返回的响应头中没有Cache-ControlExpires
  • Cache-Control:max-ageExpires导致的缓存失效
  • Cache-Control: no-cache

当存在以上三种情况时,第二次请求服务器就会进入协商缓存流程。协商缓存过程也涉及了HTTP状态码的改变,当请求发现服务器存在缓存或且缓存没有更新时,更新缓存时间并返回304 Not Modified;如果缓存失效,则服务器会把最新资源的完整版返回给浏览器,状态码为200 OK。看到这里大概就已经明白了协商缓存的过程,但细想一下还有一些没有说清楚的地方,服务器是如何校验缓存的新鲜度的呢?

Last-Modified/If-Modified-Since

当浏览器首次请求服务器资源时,服务器会将请求资源的最新修改时间Last-Modified: Tue, 07 Apr 2020 06:24:10 GMT通过响应头部返回给浏览器,浏览器在下次发起同一资源请求时会带上该信息,通过在请求头部设置If-Modified-Since: Tue, 07 Apr 2020 06:24:10 GMT,服务器会比对Last-Modified/If-Modified-Since如果服务器的资源有更新,则将最新的资源返回给浏览器,并更新响应头中的Last-Modified值,此时响应状态码为200 OK;如果服务器资源没有更新(两者时间一致),浏览器直接使用缓存即可,此时响应状态码为304 Not Modified

ETag/If-None-Match/If-Match

理论上通过比对资源的最新更新时间即可判断缓存是否有效,但实际操作中还是会遇到问题,比如资源文件在一秒内多次修改、资源经过编辑但没有发生实质内容修改的情况,这些问题表明以Last-Modified来判断还不够精确,我们需要引入ETag字段。大体流程上ETagLast-Modified的校验差不多,区别在于ETag不使用文件修改时间,而是使用文件内容摘要算出来的hash值进行判断,只要资源实质发生修改就会刷新hash值。服务器通过响应头中的ETag告知浏览器文件缓存信息,那么浏览器是如何把ETag的信息发送给服务器的呢?有以下两种形式:

  • If-None-Match: ETag value:通知服务器,如果没有匹配上,则需要重新发送资源;如果匹配上则直接返回304 Not Modified。一般浏览器使用该字段来回传ETag值。
  • If-Match: ETag value:通知服务器,如果没有匹配上ETag,或当前ETag: *但没有资源实体,则返回412 Precondition Failed;如果匹配上了,则服务器不做任何处理。

采用分布式服务器(如CDN)时,需要保证各服务器上的ETag算法一致,才不会出现A服务器与B服务器同一文件不同ETag值的出现。

Pragma

在整理资料的时候还发现了这个字段,该字段是HTTP/1.0的遗留之物,按辈分讲与Expires平齐。该字段用来定义是否需要缓存,可选值只有Pragma: no-cache,这个就可以按照字面意义理解成不进行缓存,即每次都会请求服务器的资源。优先级来讲Pragma > Cache-Control > Expires,当然,现代规范还是推荐使用Cache-Control的。

你可能感兴趣的:(从需求看HTTP缓存)