我们用http访问时,会先发送一个请求,之后服务器返回一个应答,在Chrome的开发者工具(按F12或右击选择检查)中展现了整个过程:
第一部分General是概要,包含请求地址,请求方式,状态码,服务器地址以及Referrer 策略。
第二部分是应答头部,是服务器返回的。
第三部分是请求头部,是客户端发送的。
客户端直接从源站点获取数据,当服务器访问量大时会影响访问速度,进而影响用户体验,且无法保证客户端与源站点间的距离足够短,适合传输数据。
CDN解决的正是如何将数据快速可靠地从源站点传递到客户端,通过CDN对数据的分发,用户可以从一个距离较近的服务器获取数据,而不是源站点,从而达到快速访问、且能减少源站点负载压力的目的。
CDN:Content Delivery Network/Content Ddistribute Network,即内容分发网络
客户端访问网站的过程:
1、用户在浏览器访问栏中输入要访问的域名;
2、浏览器向DNS服务器请求对该域名的解析;
3、DNS服务器返回该域名的IP地址给浏览器
4、浏览器使用该IP地址向服务器请求内容。
5、服务器将用户请求的内容返回给浏览器。
1、用户在浏览器中输入要访问的域名。
2、浏览器向DNS服务器请求对域名进行解析。由于CDN对域名解析进行了调整,DNS服务器会最终将域名的解析权交给CNAME指向的CDN专用DNS服务器。
3、CDN的DNS服务器将CDN的负载均衡设备IP地址返回给用户。
4、用户向CDN的负载均衡设备发起内容URL访问请求。
5、CDN负载均衡设备会为用户选择一台合适的缓存服务器提供服务。
选择的依据包括:根据用户IP地址,判断哪一台服务器距离用户最近;根据用户所请求的URL中携带的内容名称,判断哪一台服务器上有用户所需内容;查询各个服务器的负载情况,判断哪一台服务器的负载较小。
基于以上这些依据的综合分析之后,负载均衡设置会把缓存服务器的IP地址返回给用户。
6、用户向缓存服务器发出请求。
7、缓存服务器响应用户请求,将用户所需内容传送到用户。
如果这台缓存服务器上并没有用户想要的内容,而负载均衡设备依然将它分配给了用户,那么这台服务器就要向它的上一级缓存服务器请求内容,直至追溯到网站的源服务器将内容拉取到本地。
没有CDN:浏览器缓存
使用了CDN:浏览器缓存+CDN缓存
在用户第一次访问网站后,网站的一些静态资源如图片等就会被下载到本地,作为缓存,当用户第二次访问该网站的时候,浏览器就会从缓存中加载资源,不用向服务器请求资源,从而提高了网站的访问速度,而若使用了CDN,当浏览器本地缓存的资源过期之后,浏览器不是直接向源站点请求资源,而是向CDN边缘节点请求资源,CDN边缘节点中也存在缓存,若CDN中的缓存也过期,那就由CDN边缘节点向源站点发出回源请求来获取最新资源。
浏览器在加载资源时,先根据这个资源的一些http header判断它是否命中强缓存,如果命中,浏览器直接从自己的缓存中读取资源,不会发请求到服务器,当强缓存没有命中的时候,浏览器一定会发送一个请求到服务器,服务器端依据资源的另外一些http header验证这个资源是否命中协商缓存,如果命中,服务器会将这个请求返回,但是不会返回这个资源的数据,而是告诉客户端可以直接从缓存中加载这个资源,于是浏览器还是从自己的缓存中加载资源,当协商缓存也没有命中的时候,浏览器直接从服务器加载资源数据。
CDN节点缓存机制在不同服务商中是不同的,但一般都遵循HTTP协议,通过http响应头中的Cache-Control:max-age的字段来设置CDN节点文件缓存时间。当客户端向CDN节点请求数据时,CDN会判断缓存数据是否过期,若没有过期,则直接将缓存数据返回给客户端,否则就向源站点发出请求,从源站点拉取最新数据,更新本地缓存,并将最新数据返回给客户端。CDN服务商一般会提供基于文件后缀、目录多个维度来指定CDN缓存时间,为用户提供更精细化的缓存管理。CDN缓存时间会对“回源率”产生直接的影响,若CDN缓存时间短,则数据经常失效,导致频繁回源,增加了源站的负载,同时也增大了访问延时;若缓存时间长,数据更新时间慢,因此需要针对不同的业务需求来选择特定的数据缓存管理。
1、在地址栏中输入网址后按回车或者点击转到按钮
浏览器以最少的请求来获取网页的数据,浏览器会对所有没有过期的内容直接使用本地缓存即使用强缓存,从而减少了对服务器的请求,Expires、max-age标志只对这种方式有效。
2、按F5或浏览器刷新按钮
浏览器会在请求中附加比要的缓存协商,但不允许浏览器直接使用本地缓存即跳过强缓存的判断,直接进行协商缓存的判断,Last-Modified、ETag在这种方式发挥作用。
3、按Ctrl+F5或按Ctrl并点击刷新按钮
强制刷新,完全不使用缓存
CDN节点对开发者时透明的,可以通过CDN服务商提供的“刷新缓存”接口来达到清理CDN节点缓存的效果,强制使数据过期,从而获取到最新的数据。
当浏览器对某个资源的请求命中了强缓存时,返回的http状态码为200,在chrome开发者工具中的network中的size会显示from cache
强缓存时利用Pragma,
Expires【http1.0 时代】或者Cache-Control这些http header实现的,都用来表示资源在客户端缓存的有效期
(1)Pragma
是http1.0提出的一个header,当该字段值为no-cache
的时候(事实上现在RFC中也仅标明该可选值),会知会客户端不要对该资源读缓存,即每次都得向服务器发一次请求才行。
(2)有了Pragma来禁用缓存,自然也需要有个东西来启用缓存和定义缓存时间,对http1.0而言,Expires就是做这件事的首部字段。 Expires描述的是一个绝对时间,由服务器返回,用GMT格式的字符串表示,如Exprires:Thu,31 Dec 2037 23:55:55 GMT
(3)针对上述的“Expires时间是相对服务器而言,无法保证和客户端时间统一”的问题,http1.1新增了 Cache-Control 来定义缓存过期时间。注意:若报文中同时出现了 Expires 和 Cache-Control,则以 Cache-Control 为准。
也就是说优先级从高到低分别是 Pragma -> Cache-Control -> Expires 。
缓存过程:
1、浏览器第一次跟服务器请求一个资源,服务器在返回这个资源的同时,在response的header加上Expires的header
2、浏览器在接收到这个资源后,会把这个资源连同所有的response header一起缓存下来,所以缓存命中的请求返回的header并不是来自服务器,而是来自之前缓存的header
3、浏览器再请求这个资源时,先从缓存中寻找,找到这个资源后,拿出Expires跟当前的请求时间比较,如果请求时间在Expires指定的时间之前,就能命中缓存,否则就不行。
4、如果缓存没有命中,浏览器直接从服务器加载资源时,Expires Header在重新加载的时候会被更新
Expires是服务器返回的一个绝对时间,在服务器时间与客户端时间相差较大时,缓存管理容易出现问题,比如随意修改下客户端时间,就能影响缓存命中的结果,所以在http1.1的时候,提出了一个新的header,也就是Cache-Control,这是一个相对时间,在进行缓存命中的时候,都是利用客户端时间进行判断,因此更有效安全一些,在配置缓存的时候,以秒为单位,用数值表示:如:Cache-Control:max-age=315360000,它的缓存过程是:
1、浏览器第一次跟服务器请求一个资源,服务器在返回这个资源的同时,在response的header加上Cache-Control的header
2、浏览器在接收到这个资源的时候,会把这个资源连同所有response header一起缓存下来
3、浏览器再次请求这个资源的时候,先从缓存中寻找,找到这个资源之后,再拿这个过期时间跟当前的请求时间比较,如果请求时间在过期时间之前,就能命中缓存,否则就不行。
4、如果缓存没有命中,浏览器直接从服务器加载资源时,Cache-Control在重新加载的时候会被更新
这两个header可以只用一个,也可以同时用两个,同时存在时,Cache-Control优先级高于Expires
两种方式来设置是否启用强缓存:
1、通过代码的方式,在web服务器返回的响应中添加Expires和Cache-Control Header
2、通过配置web服务器的方式,让web服务器在响应资源的时候统一添加Expires和Cache-Control Header
通过Expires ,Cache-Control (max-age)控制,客户端发现如果上次请求的页面还未过期,通过Expires或者Cache-Control进行辨别,则直接显示本机缓存的内容,不与服务器进行通信。
Http协议的cache-control的常见取值及其组合释义:
no-cache: 数据内容不能被缓存, 每次请求都重新访问服务器, 若有max-age, 则缓存期间不访问服务器.
no-store: 不仅不能缓存, 连暂存也不可以(即: 临时文件夹中不能暂存该资源)
private(默认): 只能在浏览器中缓存, 只有在第一次请求的时候才访问服务器, 若有max-age, 则缓存期间不访问服务器.
public: 可以被任何缓存区缓存, 如: 浏览器、服务器、代理服务器等
max-age: 相对过期时间, 即以秒为单位的缓存时间.
no-cache, private: 打开新窗口时候重新访问服务器, 若设置max-age, 则缓存期间不访问服务器.
private, 正数的max-age: 后退时候不会访问服务器
no-cache, 正数的max-age: 后退时会访问服务器
点击刷新: 无论如何都会访问服务器.
-------------------------------------------------------
浏览器一般缓存图片、CSS、JS等静态文件,因为这些文件的更新频率相对来说比较低,合理利用浏览器的缓存对网站的性能提升有很大帮助。
HTTP缓存分为两部分,分别是本地缓存和缓存协商,当本地缓存不生效时会启用缓存协商。HTTP缓存主要由HTTP协议的头(Header)信息来制定。
本地缓存是指当浏览器请求资源时,如果命中了浏览器本地的缓存资源,那么浏览器就不会发送真正请求给服务器。它的执行过程是:
1. 第一次浏览器发送请求给服务器时,此时浏览器还没有本地缓存副本,服务器返回资源给浏览器,响应码是200 OK,浏览器收到资源后,把资源和对应的响应头一起缓存下来。
2. 第二次浏览器准备发送请求给服务器时候,浏览器会先检查上一次服务端返回的响应头信息中的Cache-Control(它的值是一个相对值,单位为秒,表示资源在客户端缓存的最大有效期),过期时间为第一次请求的时间加上Cache-Control的值,过期时间跟当前的请求时间比较,如果本地缓存资源没过期,那么命中缓存,不再请求服务器。
3. 如果没有命中,浏览器就会把请求发送给服务器,进入缓存协商阶段。
与本地缓存相关的头有:Cache-Control、Expires,Cache-Control有多个可选值代表不同的意义,而Expires就是一个日期格式的绝对值。
CACHE-CONTROL
Cache-Control是HTTP缓存策略中最重要的头信息,它是HTTP/1.1中出现的,它由如下几个值 no-cache:不使用本地缓存。需要使用缓存协商,先与服务器确认返回的响应是否被更改,如果之前的响应中存在ETag,那么请求的时候会与服务端验证,如果资源未被更改,则可以避免重新下载。 no-store:直接禁止游览器缓存数据,每次用户请求该资源,都会向服务器发送一个请求,每次都会下载完整的资源。 public:可以被所有的用户缓存,包括终端用户和CDN等中间代理服务器。 private:只能被终端用户的浏览器缓存,不允许CDN等中继缓存服务器对其缓存。 * max-age:从当前请求开始,允许获取的响应被重用的最长时间(秒)。
例如:
Cache-Control:public, max-age=1000
表示资源可以被所有用户以及代理服务器缓存,最长时间为1000秒。
EXPIRES
Expires是HTTP/1.0出现的头信息,同样是用于决定本地缓存策略的头,它是一个绝对时间,时间格式是如Mon, 10 Jun 2015 21:31:12 GMT,只要发送请求时间是在Expires之前,那么本地缓存始终有效,否则就会去服务器发送请求获取新的资源。如果同时出现Cache-Control:max-age和Expires,那么max-age优先级更高。他们可以这样组合使用:
Cache-Control:public
Expires: Mon, 10 Jun 2015 21:31:12 GMT
强缓存是前端性能优化最有力的工具,对于有大量静态资源的网页,一定要利用强缓存,提高响应速度,通常是为这些静态资源全部配置一个超时时间超长的Expires或Cache-Control,这样用户只会在第一次访问网站时加载静态资源,其他时间只要缓存没有失效并且用户没有强制刷新的条件下都会从缓存中加载。
然而这种缓存配置方式会带来一个问题,就是当资源更新时,客户端由于有缓存不会向服务器请求最新的资源,这个问题已有解决方案:
通过更新页面中引用的资源路径,让浏览器主动放弃缓存,加载新资源。
解决方法:
1、ctrl+F5
2、浏览器隐私模式开发
3、chrome开发者工具里将Disable cache选项打勾,阻止缓存
4、在开发阶段,给资源加上一个动态的参数,由于每次资源的修改都要更新引用的位置,同时修改参数的值,所以操作起来不是很方便,除非是在动态页面比如jsp里开发就可以用服务器变量来解决,或者用前端构建工具来处理这个参数修改的问题。
5、如果资源引用的页面被嵌入到了一个iframe里面,可以在iframe的区域右键重新加载该页面
6、如果缓存问题出现在ajax请求中,最有效的解决办法就是ajax的请求地址追加随机数
7、动态设置iframe的src时,有可能因为缓存问题导致看不到最新效果,在src后面添加随机数即可
8、通过前端开发工具grunt gulp等的插件来启动一个静态服务器,则在这个服务器下所有资源返回的response header中,Cache-Control始终被设置为不缓存
发布问题:若页面和它引用的资源路径同时更新了,不管是先部署页面还是先部署资源都会带来各种问题,这是由于资源是覆盖式发布的,即用待发布资源覆盖已发布资源。
解决办法就是实现非覆盖式发布:把有修改的资源文件作为一个新的文件发布,不对已有的资源文件进行覆盖,这样用户还可以请求旧的资源文件,不至于发生页面错乱的问题,这样先部署静态资源,再覆盖式部署页面,等到用户访问新页面的时候,新的资源文件也已发布,就可以正确请求,即可解决问题。
如果命中协商缓存,请求响应返回的http状态为304以及一个Not Modified字符串,协商缓存利用的是【Last-Modified、If-Modified-Since】、【ETag、If-None-Match】这两对header来管理的。
5.1【Last-Modified、If-Modified-Since】:
1、浏览器第一次跟服务器请求一个资源,服务器在返回这个资源时,在response的header加上Last-Modified的header,表示这个资源在服务器上的最后修改时间
2、浏览器再次向服务器请求这个资源时,在request的header加上If-Modified-Since的header,这个header的值就是上一次请求时返回的Last-Modified的值
3、服务器再次收到资源请求时,根据浏览器传过来If-Modified-Since和资源在服务器上的最后修改时间判断资源是否有变化,如果没有变化则返回304 Not Modified,但是不会返回资源内容,如果有变化就返回资源内容,当服务器返回304 Not Modified的响应时,response header中不会再添加Last-Modified的header,因为资源没有变化,Last-Modified的值也不变
4、浏览器收到304的响应后,就会从缓存中加载资源
5、如果协商缓存没有命中,浏览器直接从服务器加载资源时,Last-Modofied header在重新加载的时候会被更新,下次请求时,If-Modified-Since会采用上一次返回的Last-Modified的值
这一对header都是根据服务器时间返回的,有时候会有服务器资源有变化,但最后修改时间却没有变化的情况,因此有了
5.2【Etag、If-None-Match】:
1、浏览器第一次向服务器请求一个资源,服务器在返回这个资源的同时,在response的header加上ETag的header,这个header是服务器根据当前请求的资源生成的一个唯一标识,是一个字符串,只要资源内容发生改变,这个字符串也会改变,跟时间没有关系
2、浏览器再次请求这个资源的时候,在request的header上加上If-None-Match的header。这个header的值是上一次请求返回的ETag的值
3、服务器再次收到资源请求时,根据客户端传过来的If-None-Match和重新生成的该资源的新的ETag做比较,相同则返回304 Not Modified,不会返回资源内容,如果不同则返回资源内容,但这里即使资源没有发生变化,也会返回ETag,因为这个ETag重新生成过,即使没有ETag没有变化
4、浏览器收到304响应后,就从缓存中加载资源
通过Last-Modified,If-Modified-Since方式和服务器通信,客户发出http请求中包含If-Modified-Since,如果服务器端代码没有修改,服务器端返回304响应代码的请求响应头(内容不返回),客户端则直接用本机缓存的内容缓存显示结果。相当于节省了服务器执行代码时间以及数据传输时间。
缓存协商
当第一次请求时服务器返回的响应头中没有Cache-Control和Expires或者Cache-Control和Expires过期抑或它的属性设置为no-cache时,那么浏览器第二次请求时就会与服务器进行协商,询问浏览器中的缓存资源是不是旧版本,需不需要更新,此时,服务器就会做出判断,如果缓存和服务端资源的最新版本是一致的,那么就无需再次下载该资源,服务端直接返回304 Not Modified 状态码,如果服务器发现浏览器中的缓存已经是旧版本了,那么服务器就会把最新资源的完整内容返回给浏览器,状态码就是200 Ok,那么服务端是根据什么来判断浏览器的缓存是不是最新的呢?其实是根据HTTP的另外两组头信息,分别是:Last-Modified/If-Modified-Since 与 ETag/If-None-Match。
LAST-MODIFIED与IF-MODIFIED-SINCE
浏览器第一次请求资源时,服务器会把资源的最新修改时间Last-Modified:Thu, 29 Dec 2011 18:23:55 GMT放在响应头中返回给浏览器,第二次请求时,浏览器就会把上一次服务器返回的修改时间放在请求头If-Modified-Since:Thu, 29 Dec 2011 18:23:55发送给服务器,服务器就会拿这个时间跟服务器上的资源的最新修改时间进行对比,如果两者相等或者大于服务器上的最新修改时间,那么表示浏览器的缓存是有效的,此时缓存会命中,服务器就不再返回内容给浏览器了,同时Last-Modified头也不会返回,因为资源没被修改,返回了也没什么意义。如果没命中缓存则最新修改的资源连同Last-Modified头一起返回。
第一次请求返回的响应头:
Cache-Control:max-age=3600
Expires:Tue, 26 Jan 2016 08:28:52 GMT
Last-Modified:Fri, 15 Jan 2016 12:06:06 GMT
第二次请求的请求头信息:
If-Modified-Since:Fri, 15 Jan 2016 12:06:06 GMT
这组头信息是基于资源的修改时间来判断资源有没有更新,另一种方式就是根据资源的内容来判断,就是接下来要讨论的ETag与If-None-Match
ETAG与IF-NONE-MATCH
ETag/If-None-Match与Last-Modified/If-Modified-Since的流程其实是类似的,唯一的区别是它基于资源的内容的摘要信息(比如MD5 hash)来判断。浏览器发送第二次请求时,会把第一次的响应头信息ETag的值放在If-None-Match的请求头中发送到服务器,与最新的资源的摘要信息对比,如果相等,取浏览器缓存,否则内容有更新,最新的资源连同最新的摘要信息返回。用ETag的好处是如果因为某种原因到时资源的修改时间没改变,那么用ETag就能区分资源是不是有被更新。
第一次请求返回的响应头:
Cache-Control:public, max-age=31536000
ETag: "15f0fff99ed5aae4edffdd6496d7131f"
第二次请求的请求头信息:
If-None-Match: "15f0fff99ed5aae4edffdd6496d7131f"
===============================================================
浏览器缓存机制,其实主要就是HTTP协议定义的缓存机制(如: Expires; Cache-control等)。但是也有非HTTP协议定义的缓存机制,如使用HTML Meta 标签,Web开发者可以在HTML页面的
节点中加入标签,代码如下:html code
上述代码的作用是告诉浏览器当前页面不被缓存,每次访问都需要去服务器拉取。使用上很简单,但只有部分浏览器可以支持,而且所有缓存代理服务器都不支持,因为代理不解析HTML内容本身。
Cache-control策略(重点关注)
Cache-Control与Expires的作用一致,都是指明当前资源的有效期,控制浏览器是否直接从浏览器缓存取数据还是重新发请求到服务器取数据。只不过Cache-Control的选择更多,设置更细致,如果同时设置的话,其优先级高于Expires。
http协议头Cache-Control :
值可以是public、private、no-cache、no- store、no-transform、must-revalidate、proxy-revalidate、max-age
各个消息中的指令含义如下:
Public指示响应可被任何缓存区缓存。
Private指示对于单个用户的整个或部分响应消息,不能被共享缓存处理。这允许服务器仅仅描述当用户的部分响应消息,此响应消息对于其他用户的请求无效。
no-cache指示请求或响应消息不能缓存
no-store用于防止重要的信息被无意的发布。在请求消息中发送将使得请求和响应消息都不使用缓存。
max-age指示客户机可以接收生存期不大于指定时间(以秒为单位)的响应。
min-fresh指示客户机可以接收响应时间小于当前时间加上指定时间的响应。
max-stale指示客户机可以接收超出超时期间的响应消息。如果指定max-stale消息的值,那么客户机可以接收超出超时期指定值之内的响应消息。
Last-Modified/If-Modified-Since
Last-Modified/If-Modified-Since要配合Cache-Control使用。
l Lalast-Modified:标示这个响应资源的最后修改时间。web服务器在响应请求时,告诉浏览器资源的最后修改时间。
l If-If-Modified-Since:当资源过期时(使用Cache-Control标识的max-age),发现资源具有Last-Modified声明,则再次向web服务器请求时带上头 If-Modified-Since,表示请求时间。web服务器收到请求后发现有头If-Modified-Since 则与被请求资源的最后修改时间进行比对。若最后修改时间较新,说明资源又被改动过,则响应整片资源内容(写在响应消息包体内),HTTP 200;若最后修改时间较旧,说明资源无新修改,则响应HTTP 304 (无需包体,节省浏览),告知浏览器继续使用所保存的cache。
Etag/If-None-Match
Etag/If-None-Match也要配合Cache-Control使用。
l EtEtag:web服务器响应请求时,告诉浏览器当前资源在服务器的唯一标识(生成规则由服务器觉得)。Apache中,ETag的值,默认是对文件的索引节(INode),大小(Size)和最后修改时间(MTime)进行Hash后得到的。
l If-None-Match:当资源过期时(使用Cache-Control标识的max-age),发现资源具有Etage声明,则再次向web服务器请求时带上头If-None-Match (Etag的值)。web服务器收到请求后发现有头If-None-Match 则与被请求资源的相应校验串进行比对,决定返回200或304。
既生Last-Modified何生Etag?
你可能会觉得使用Last-Modified已经足以让浏览器知道本地的缓存副本是否足够新,为什么还需要Etag(实体标识)呢?HTTP1.1中Etag的出现主要是为了解决几个Last-Modified比较难解决的问题:
l Last-Modified标注的最后修改只能精确到秒级,如果某些文件在1秒钟以内,被修改多次的话,它将不能准确标注文件的修改时间
l 如果某些文件会被定期生成,当有时内容并没有任何变化,但Last-Modified却改变了,导致文件没法使用缓存
l 有可能存在服务器没有准确获取文件修改时间,或者与代理服务器时间不一致等情形
Etag是服务器自动生成或者由开发者生成的对应资源在服务器端的唯一标识符,能够更加准确的控制缓存。Last-Modified与ETag是可以一起使用的,服务器会优先验证ETag,一致的情况下,才会继续比对Last-Modified,最后才决定是否返回304。
用户行为与缓存
浏览器缓存行为还有用户的行为有关!!!