腾讯DeepOcean原创文章:dopro.io/http-cache-…css
配置http缓存与cdn缓存一直以来都是web性能优化中重要而常见的手段。合理的http缓存与cdn缓存配置能够起到减轻服务器压力,缓解网络瓶颈,提高用户体验等做用,不当的缓存配置却会致使资源没法及时更新,用户体验差别,甚至流程出错等问题。本文主要讲解http缓存与cdn缓存的原理和配置规则,但愿经过本文的讲解可以让你们清楚什么是合理的缓存配置,如何为本身的项目定制化缓存方案,以及若是碰到缓存问题,应该如何分析解决。html
首先,让咱们来看这样一个场景
项目A上线了一个新特性,包含着逻辑的改动和页面UI的更新,小明做为项目开发将代码提交后进行了预发布。产品经理小红开始体验新特性,奇怪的是,小红进入项目后却并无看到最新的特性,这时小明思考了一会说,小红你点击刷新再试试,果真,刷新后项目有了变化,新特性出来了,可是这时又有了新的问题,项目里的图片彷佛仍是旧图,小明又思考了一会,随后在电脑前捣鼓一番,让小红再次体验,终于,这个时候特性完整验收经过。上述的案例中,其实就包含着http缓存和cdn缓存的应用,固然,这是一个反面教材,实际上线过程当中,咱们不可能让每个用户点击刷新来体验咱们的新特性,那应该如何解决上述问题呢,接下来上干货。前端
http缓存
简介
http缓存是客户端缓存,浏览器做为客户端接受到服务端响应后,对于响应首部字段进行解析,分析出相应的缓存规则,将资源按规则进行缓存,再次请求时若是命中缓存则直接读取本地缓存再也不发出请求。webpack
缓存规则
http缓存规则由响应首部字段进行控制,其中的关键字段有Expires,Cache-Control ,Last-Modified ,Etag 四个字段,Expires和Cache-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控制台能够很轻松的找到一个案例:

图中配置web
- cache-control: max-age=31535000 表明相对于请求时间,缓存31536000秒,即365天,在此时间内,再次访问资源直接读取本地缓存,不向服务器发送请求.
- last-modified: Mon...上次修改时间,若是缓存时间过时,该字段将用于与请求中的If-Modified-Since字段进行对比,一致则继续使用以前缓存,不一致则认定缓存失效
- expires: 在http1.0版本下被cache-control覆盖,此处意为缓存至Sat, 11 Aug ...
缓存流程
缓存规则在其中是如何起做用的呢,咱们来看几个重点关注部分chrome
重点关注1: 缓存是否过时gulp
基于该资源上次响应缓存规则同时知足下列条件则视为缓存未过时。须要注意的是,判断缓存是否过时只跟客户端有关系,与服务端无关。1&2&3同时知足即认为缓存未过时,相反则是已过时浏览器
- cache-control值为max-age
- max-age > 0
- 当前 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为准具体状况以下:
- cache-control=no-store 不缓存任何资源
- cache-control=no-cache 缓存但当即过时
- cache-control=max-age(s-maxage) = 0 缓存但当即过时(等同于no-cache)
- cache-control=max-age(s-maxage)= seconds (seconds > 0)—— 基于请求时间缓存seconds秒
- 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.
- expires = 过去的时间或无效时间,缓存但当即过时,等同于cache-control=no-cache
- expires = 将来的时间,缓存到对应时间
缓存配置
从上述规则和与流程图中咱们能够看到,缓存规则的配置其实并不复杂,除开Etag和Last-Modified用于缓存对比(实际使用中只须要开启该功能便可),咱们须要关注的其实只是cache-control(expires可转化为max-age形式,再也不赘述),方案以下:
- cache-control: no-store:不缓存,每次访问都从服务下载全部资源。
- cache-control: no-cache或cache-control: max-age=0:对比缓存,缓存当前资源,但每次访问都须要跟服务器对比,检查资源是否被修改。
- 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缓存的价值。
cdn缓存
cdn缓存是一种服务端缓存,CDN服务商将源站的资源缓存到遍及全国的高性能加速节点上,当用户访问相应的业务资源时,用户会被调度至最接近的节点最近的节点ip返回给用户,在web性能优化中,它主要起到了,缓解源站压力,优化不一样用户的访问速度与体验的做用。
缓存规则
与http缓存规则不一样的是,这个规则并非规范性的,而是由cdn服务商来制定,咱们以腾讯云举例,打开cdn加速服务配置,面板以下。
能够看到,提供给咱们的配置项只有文件类型(或文件目录)和刷新时间,意义也很简单,针对不一样文件类型,在cdn节点上缓存对应的时间。
cdn运做流程
由图咱们能够看出,cdn缓存的配置主要做用在缓存处理阶段,虽然配置项只有文件类型和缓存时间,但流程却并不简单,咱们先来明确一个概念——回源,回源的意思就是返回源站,何为源站,就是咱们本身的服务器,不少人误解接入cdn就是把资源放在了cdn上,其实否则,如图中所示,接入cdn后,咱们的服务器就是源站,源站通常状况下只会在cdn节点没有资源或cdn资源失效时接收到cdn节点的请求,其余时间,源站并不会接收请求(固然,若是咱们知道源站的地址,咱们能够直接访问源站)。明确了回源的概念后,cdn的流程就显得不那么复杂了,简单的理解就是,没有资源就去源站读取,有资源就直接发送给用户。与http缓存不一样的是,cdn中没有no-cache(max-age=0)的状况,当咱们设置缓存时间为0的时候,该类型文件就被认定为不缓存文件,就是全部请求直接转发源站,只有当缓存时间大于0且缓存过时的时候,才会与源站对比缓存是否被修改。
缓存配置
cdn缓存配置并不麻烦,总体来讲,建议和http缓存配置保持统一。须要特别注意的是,cdn的缓存配置会受到http缓存配置的影响,并且各个cdn服务商并不彻底一致,以腾讯云为例,在缓存配置的文档中特别有如下说明。
这会对咱们有什么影响呢?
- 若是咱们http缓存设置cache-control: max-age=600,即缓存10分钟,但cdn缓存配置中设置文件缓存时间为1小时,那么就会出现以下状况,文件被访问后第12分钟修改并上传到服务器,用户从新访问资源,响应码会是304,对比缓存未修改,资源依然是旧的,一个小时后再次访问才能更新为最新资源
- 若是不设置cache-control呢,在http缓存中咱们说过,若是不设置cache-control,那么会有默认的缓存时间,但在这里,cdn服务商明确会在没有cache-control字段时主动帮咱们添加cache-control: max-age=600。
注:针对问题1,也并不是没有办法,当咱们必需要在缓存期内修改文件,而且不向想影响用户体验,那么咱们可使用cdn服务商提供的强制更新缓存功能,主要注意的是,这里的强制更新是更新服务端缓存,http缓存依然按照http头部规则进行本身的缓存处理,并不会受到影响。
小结
cdn缓存的配置并不复杂, 复杂的状况在于cdn缓存配置会受到http缓存配置的影响,而且不一样的cdn运营商对于这种影响的处理也都不一致,实际使用时,建议去对应的cdn服务商文档中找到对应的注意事项。
http缓存与cdn缓存的结合
当咱们分别理解了http缓存配置和cdn缓存配置后,咱们还有一件事情,就是理解两者结合时,请求的流向问题

当用户访问咱们的业务服务器时,首先进行的就是http缓存处理,若是http缓存经过校验,则直接响应给用户,若是未经过校验,则继续进行cdn缓存的处理,cdn缓存处理完成后返回给客户端,由客户端进行http缓存规则存储并响应给用户。当咱们分析缓存问题时,必定要将两个流程独立开来分析,如今来看开篇时的错误案例,很明显,第一个问题时因为http缓存配置不合理,致使用户必须进行强制刷新才能更新资源,第二个问题则是cdn缓存未及时更新形成的。
总结
http缓存和cdn缓存分别做为客户端缓存和服务端缓存共同影响着咱们的web请求流向,要想作好缓存配置,首先是清楚缓存的原理和配置规则,其次则是结合项目分析缓存级别,具体状况具体处理。
欢迎关注"腾讯DeepOcean"微信公众号,每周为你推送前端、人工智能、SEO/ASO等领域相关的原创优质技术文章:
看小编搬运这么辛苦,关注一个呗:)