HTTP缓存机制

Web 缓存大体能够分为:数据库缓存、服务器端缓存(代理服务器缓存、CDN 缓存)、浏览器缓存。node

浏览器缓存也包含不少内容: HTTP 缓存、indexDB、cookie、localstorage 等等。这里咱们只讨论 HTTP 缓存相关内容。数据库

在具体了解 HTTP 缓存以前先来明确几个术语:浏览器

  • 缓存命中率:从缓存中获得数据的请求数与全部请求数的比率。理想状态是越高越好。
  • 过时内容:超过设置的有效时间,被标记为“陈旧”的内容。一般过时内容不能用于回复客户端的请求,必须从新向源服务器请求新的内容或者验证缓存的内容是否仍然准备。
  • 验证:验证缓存中的过时内容是否仍然有效,验证经过的话刷新过时时间。
  • 失效:失效就是把内容从缓存中移除。当内容发生改变时就必须移除失效的内容。

浏览器缓存主要是 HTTP 协议定义的缓存机制。HTML meta 标签,例如缓存

<META HTTP-EQUIV="Pragma" CONTENT="no-store">服务器

含义是让浏览器不缓存当前页面。可是代理服务器不解析 HTML 内容,通常应用普遍的是用 HTTP 头信息控制缓存。cookie

 

浏览器缓存分类函数

浏览器缓存分为强缓存和协商缓存,浏览器加载一个页面的简单流程以下:工具

  1. 浏览器先根据这个资源的http头信息来判断是否命中强缓存。若是命中则直接加在缓存中的资源,并不会将请求发送到服务器。
  2. 若是未命中强缓存,则浏览器会将资源加载请求发送到服务器。服务器来判断浏览器本地缓存是否失效。若可使用,则服务器并不会返回资源信息,浏览器继续从缓存加载资源。
  3. 若是未命中协商缓存,则服务器会将完整的资源返回给浏览器,浏览器加载新资源,并更新缓存。

 

强缓存url

命中强缓存时,浏览器并不会将请求发送给服务器。在Chrome的开发者工具中看到http的返回码是200,可是在Size列会显示为(from cache)。
spa

强缓存是利用http的返回头中的Expires或者Cache-Control两个字段来控制的,用来表示资源的缓存时间。

Expires

缓存过时时间,用来指定资源到期的时间,是服务器端的具体的时间点。也就是说,Expires=max-age + 请求时间,须要和Last-modified结合使用。但在上面咱们提到过,cache-control的优先级更高。 Expires是Web服务器响应消息头字段,在响应http请求时告诉浏览器在过时时间前浏览器能够直接从浏览器缓存取数据,而无需再次请求。

 

该字段会返回一个时间,好比Expires:Thu,31 Dec 2037 23:59:59 GMT。这个时间表明着这个资源的失效时间,也就是说在2037年12月31日23点59分59秒以前都是有效的,即命中缓存。这种方式有一个明显的缺点,因为失效时间是一个绝对时间,因此当客户端本地时间被修改之后,服务器与客户端时间误差变大之后,就会致使缓存混乱。因而发展出了Cache-Control。

Cache-Control

Cache-Control是一个相对时间,例如Cache-Control:3600,表明着资源的有效期是3600秒。因为是相对时间,而且都是与客户端时间比较,因此服务器与客户端时间误差也不会致使问题。
Cache-Control与Expires能够在服务端配置同时启用或者启用任意一个,同时启用的时候Cache-Control优先级高

Cache-Control 能够由多个字段组合而成,主要有如下几个取值:

1. max-age 指定一个时间长度,在这个时间段内缓存是有效的,单位是s。例如设置 Cache-Control:max-age=31536000,也就是说缓存有效期为(31536000 / 24 / 60 * 60)天,第一次访问这个资源的时候,服务器端也返回了 Expires 字段,而且过时时间是一年后。

 

在没有禁用缓存而且没有超过有效时间的状况下,再次访问这个资源就命中了缓存,不会向服务器请求资源而是直接从浏览器缓存中取。

2. s-maxage 同 max-age,覆盖 max-age、Expires,但仅适用于共享缓存,在私有缓存中被忽略。

3. public 代表响应能够被任何对象(发送请求的客户端、代理服务器等等)缓存。

4. private 代表响应只能被单个用户(多是操做系统用户、浏览器用户)缓存,是非共享的,不能被代理服务器缓存。

5. no-cache 强制全部缓存了该响应的用户,在使用已缓存的数据前,发送带验证器的请求到服务器。不是字面意思上的不缓存。

6. no-store 禁止缓存,每次请求都要向服务器从新获取数据。

七、must-revalidate指定若是页面是过时的,则去服务器进行获取。这个指令并不经常使用,就不作过多的讨论了。

协商缓存

若未命中强缓存,则浏览器会将请求发送至服务器。服务器根据http头信息中的Last-Modify/If-Modify-Since或Etag/If-None-Match来判断是否命中协商缓存。若是命中,则http返回码为304,浏览器从缓存中加载资源。

Last-Modify/If-Modify-Since

浏览器第一次请求一个资源的时候,服务器返回的header中会加上Last-Modify,Last-modify是一个时间标识该资源的最后修改时间,例如Last-Modify: Thu,31 Dec 2037 23:59:59 GMT。

当浏览器再次请求该资源时,发送的请求头中会包含If-Modify-Since,该值为缓存以前返回的Last-Modify。服务器收到If-Modify-Since后,根据资源的最后修改时间判断是否命中缓存。

若是命中缓存,则返回http304,而且不会返回资源内容,而且不会返回Last-Modify。因为对比的服务端时间,因此客户端与服务端时间差距不会致使问题。可是有时候经过最后修改时间来判断资源是否修改仍是不太准确(资源变化了最后修改时间也能够一致)。因而出现了ETag/If-None-Match。

ETag/If-None-Match

与Last-Modify/If-Modify-Since不一样的是,Etag/If-None-Match返回的是一个校验码(ETag: entity tag)。ETag能够保证每个资源是惟一的,资源变化都会致使ETag变化*。ETag值的变动则说明资源状态已经被修改。服务器根据浏览器上发送的If-None-Match值来判断是否命中缓存。

 

 

 ETag扩展说明

咱们对ETag寄予厚望,但愿它对于每个url生成惟一的值,资源变化时ETag也发生变化。神秘的Etag是如何生成的呢?以Apache为例,ETag生成靠如下几种因子

  1. 文件的i-node编号,此i-node非彼iNode。是Linux/Unix用来识别文件的编号。是的,识别文件用的不是文件名。使用命令’ls –I’能够看到。
  2. 文件最后修改时间
  3. 文件大小
    生成Etag的时候,可使用其中一种或几种因子,使用抗碰撞散列函数来生成。因此,理论上ETag也是会重复的,只是几率小到能够忽略。

既生Last-Modified何生Etag?

你可能会以为使用Last-Modified已经足以让浏览器知道本地的缓存副本是否足够新,为何还须要Etag(实体标识)呢?HTTP1.1中Etag的出现主要是为了解决几个Last-Modified比较难解决的问题:

1. Last-Modified标注的最后修改只能精确到秒级,若是某些文件在1秒钟之内,被修改屡次的话,它将不能准确标注文件的修改时间

2. 若是某些文件会被按期生成,当有时内容并无任何变化,但Last-Modified却改变了,致使文件无法使用缓存

3.有可能存在服务器没有准确获取文件修改时间,或者与代理服务器时间不一致等情形

Etag是服务器自动生成或者由开发者生成的对应资源在服务器端的惟一标识符,可以更加准确的控制缓存。Last-Modified与ETag是能够一块儿使用的,服务器会优先验证ETag,一致的状况下,才会继续比对Last-Modified,最后才决定是否返回304。

用户行为与缓存

浏览器缓存行为还有用户的行为有关!!!

用户操做

Expires/Cache-Control

Last-Modified/Etag

地址栏回车

有效

有效

页面连接跳转

有效

有效

新开窗口

有效

有效

前进、后退

有效

有效

F5刷新

无效

有效

Ctrl+F5刷新

无效

无效

总结:

浏览器第一次请求:

 

浏览器再次请求时:

相关文章
相关标签/搜索