http缓存与cdn缓存配置那些事儿

配置http缓存与cdn缓存一直以来都是web性能优化中重要而常见的手段。合理的http缓存与cdn缓存配置可以起到减轻服务器压力,缓解网络瓶颈,提升用户体验等作用,不当的缓存配置却会导致资源无法及时更新,用户体验差异,甚至流程出错等问题。本文主要讲解http缓存与cdn缓存的原理和配置规则,希望通过本文的讲解能够让大家清楚什么是合理的缓存配置,如何为自己的项目定制化缓存方案,以及如果碰到缓存问题,应该如何分析解决。

首先,让我们来看这样一个场景

项目A上线了一个新特性,包含着逻辑的改动和页面UI的更新,小明作为项目开发将代码提交后进行了预发布。产品经理小红开始体验新特性,奇怪的是,小红进入项目后却并没有看到最新的特性,这时小明思考了一会说,小红你点击刷新再试试,果然,刷新后项目有了变化,新特性出来了,但是这时又有了新的问题,项目里的图片似乎还是旧图,小明又思考了一会,随后在电脑前捣鼓一番,让小红再次体验,终于,这个时候特性完整验收通过。上述的案例中,其实就包含着http缓存和cdn缓存的应用,当然,这是一个反面教材,实际上线过程中,我们不可能让每一个用户点击刷新来体验我们的新特性,那应该如何解决上述问题呢,接下来上干货。

http缓存

简介

http缓存是客户端缓存,浏览器作为客户端接受到服务端响应后,对于响应首部字段进行解析,分析出相应的缓存规则,将资源按规则进行缓存,再次请求时如果命中缓存则直接读取本地缓存不再发出请求。

缓存规则

http缓存规则由响应首部字段进行控制,其中的关键字段有ExpiresCache-Control ,Last-Modified ,Etag 四个字段,ExpiresCache-Control用来确定确定缓存的存储时间,Last-Modified 和Etag则用来确定缓存是否要被更新,我们简单来看一下区别。

  • expires: HTTP1.0中用来控制缓存时间的参数,响应头包含日期/时间, 即在此时间之后,响应过期。
  • cache-control: HTTP1.1中用来控制缓存时间的参数
    • public: 表明响应可以被任何对象(包括:发送请求的客户端,代理服务器,等等)缓存。
    • private: 表明响应只能被单个用户缓存,不能作为共享缓存(即代理服务器不能缓存它)。
    • max-age=<seconds>: 设置缓存存储的最大周期,相对于请求的时间缓存seconds秒,在此时间内,访问资源直接读取本地缓存,不向服务器发出请求。(与expires同时出现时,max-age优先级更高)
    • s-maxage=<seconds>: 规则等同max-age,覆盖max-age 或者 Expires 头,但是仅适用于共享缓存(比如各个代理),并且私有缓存中它被忽略。(与expires或max-age同时出现时,s-maxage优先级更高)
    • no-store: 不缓存服务器响应的任何内容,每次访问资源都需要服务器完整响应
    • no-cache: 缓存资源,但立即过期,每次请求都需要跟服务器对比验证资源是否被修改。(等同于max-age=0)
  • Last-modified: 源头服务器认定的资源做出修改的日期及时间。精确度比Etag低。包含有If-Modified-Since或 If-Unmodified-Since首部的条件请求会使用这个字段。
  • Etag: HTTP响应头是资源的特定版本的标识符。

我们通过chrome控制台可以很轻松的找到一个案例:

图中配置

  1. cache-control: max-age=31535000 代表相对于请求时间,缓存31536000秒,即365天,在此时间内,再次访问资源直接读取本地缓存,不向服务器发送请求.
  2. last-modified: Mon…上次修改时间,如果缓存时间过期,该字段将用于与请求中的If-Modified-Since字段进行对比,一致则继续使用之前缓存,不一致则认定缓存失效
  3. expires: 在http1.0版本下被cache-control覆盖,此处意为缓存至Sat, 11 Aug …

缓存流程

http缓存与cdn缓存配置

缓存规则在其中是如何起作用的呢,我们来看几个重点关注部分

重点关注1: 缓存是否过期

基于该资源上次响应缓存规则同时满足下列条件则视为缓存未过期。需要注意的是,判断缓存是否过期只跟客户端有关系,与服务端无关。1&2&3同时满足即认为缓存未过期,相反则是已过期

  1. cache-control值为max-age
  2. max-age > 0
  3. 当前 date < 上次请求时的date + max-age

注:expire可同等转化为cache-control=max-age形式,不再赘述,s-maxage与maxage规则相同,不再赘述

重点关注2: 询问服务器资源是否修改

判断资源是否修改,需要客户端与服务器共同协作,客户端在首次拿到资源缓存后会存储Etag(若有)和Last-Modified(若有),在下次缓存过期时会将Etag写在请求头部中的If-None-Match中,将Last-Modified值写在请求头部中的If-Modified-Since中,服务端优先对Etag进行对比,然后再对比Last-Modified,完全通过后即视为缓存没有修改,有一项不通过则认为资源已被修改,缓存失效

重点关注3: 缓存规则

缓存规则主要由cache-control字段和expires字段体现,同时出现则以cache-control为准具体情况如下:

  1. cache-control=no-store 不缓存任何资源
  2. cache-control=no-cache 缓存但立即过期
  3. cache-control=max-age(s-maxage) = 0 缓存但立即过期(等同于no-cache)
  4. cache-control=max-age(s-maxage)= seconds (seconds > 0)—— 基于请求时间缓存seconds秒
  5. cache-control=其他 根据http标准,如果不携带任何关于缓存时常的标记,则缓存时间等于当前时间和 Last-Modified时间的差值的10%,等同于cache-control=max-age=(date – Last-Modified)/ 10,通过fiddler抓包可看到英文原文:No explicit HTTP Cache Lifetime information was provided.Heuristic expiration policies suggest defaulting to: 10% of the delta between Last-Modified and Date.
  6. expires = 过去的时间或无效时间,缓存但立即过期,等同于cache-control=no-cache
  7. expires = 未来的时间,缓存到对应时间

缓存配置

从上述规则和与流程图中我们可以看到,缓存规则的配置其实并不复杂,除开Etag和Last-Modified用于缓存对比(实际使用中只需要开启该功能即可),我们需要关注的其实只是cache-control(expires可转化为max-age形式,不再赘述),方案如下:

  1. cache-control: no-store:不缓存,每次访问都从服务下载所有资源。
  2. cache-control: no-cache或cache-control: max-age=0:对比缓存,缓存当前资源,但每次访问都需要跟服务器对比,检查资源是否被修改。
  3. cache-control: max-age=seconds //seconds > 0:强缓存,缓存当前资源,在一定时期内,再次请求资源直接读取本地缓存。

注:强缓存下资源也并非不可更新,例如chrome的ctrl + f5等同于直接触发方案1,f5或者webview的刷新键会直接触发方案2,但都是基于客户端操作,不建议纳入实际项目考虑。

实际项目中,方案1的应用基本上看不到,对比方案2和方案3,方案1没有任何优势。在方案2和方案3的选择中,我们会对资源作区分。

  • 对于img,css,js,fonts等非html资源,我们可以直接考虑方案3,并且max-age配置的时间可以尽可能久,类似于缓存规则案例中,cache-control: max-age=31535000配置365天的缓存,需要注意的是,这样配置并不代表这些资源就一定一年不变,其根本原因在于目前前端构建工具在静态资源中都会加入戳的概念(例如,webpack中的[hash],gulp中的gulp-rev),每次修改均会改变文件名或增加query参数,本质上改变了请求的地址,也就不存在缓存更新的问题。
  • 对于html资源,我们建议根据项目的更新频度来确定采用哪套方案。html作为前端资源的入口文件,一旦被强缓存,那么相关的js,css,img等均无法更新。对于高频维护的业务类项目,建议采用方案2,或是方案3但max-age设置一个较小值,例如3600,一小时过期。对于一些活动项目,上线后不会进行较大改动,建议采用方案3,不过max-age也不要设置过大,否则一旦出现bug或是未知问题,用户无法及时更新。

除了以上考虑,有时候其他因素也会影响缓存的配置,例如QQ红包除夕活动,高并发大流量很容易给服务器带来极大挑战,这时我们作为前端开发,就可以采用方案3来避免用户多次进入带来的流量压力。

对于http缓存的配置,我们始终要做到两点,一是清楚明白http缓存的原理与规则,二是明确缓存的配置不是一次性的,根据不同的情况配置不同的规则,才能够更好的发挥http缓存的价值。

  • 版权声明: 本文源自 Deep Ocean, 于5年前,由整理发表,共 5565字。
  • 原文链接:点此查看原文
你想把广告放到这里吗?

发表评论

您必须 登录 才能发表留言!