HTTP 缓存机制

HTTP缓存作为web性能优化的重要手段,对于从事web开发的朋友有重要的意义。

在HTTP中具有缓存功能的是浏览器缓存或者说客户端缓存。

1 缓存的优点:

  1. 减少了冗余的数据传递,节省宽带流量

  2. 减少了服务器的负担,大大提高了网站性能

  3. 加快了客户端加载网页的速度 这也正是HTTP缓存属于客户端缓存的原因。

2 缓存的分类

浏览器存在一个缓存数据库,用于储存一些不经常变化的静态文件(图片、css、js等)。我们将缓存分为强制缓存和协商缓存。

强制缓存

当缓存数据库中已有所请求的数据时。客户端直接从缓存数据库中获取数据。当缓存数据库中没有所请求的数据时,客户端的才会从服务端获取数据。

协商缓存

又称对比缓存,客户端会先从缓存数据库中获取到一个缓存数据的标识,得到标识后请求服务端验证是否失效(新鲜),如果没有失效服务端会返回304,此时客户端直接从缓存中获取所请求的数据,如果标识失效,服务端会返回更新后的数据。

 

 

3 缓存的方案

浏览器和服务器进行交互的时候会发送一些请求数据和响应数据,我们称之为HTTP报文。报文中包含首部header和主体部分body。与缓存相关的规则信息就包含在header中。

强制缓存的方案

现在服务器响应的header 大多数使用Cache-Control。

Cache-Control

Cache-Control有很多属性,不同的属性代表的意义也不同。 private:客户端可以缓存 public:客户端和代理服务器都可以缓存 max-age=t:缓存内容将在t秒后失效 no-cache:需要使用协商缓存来验证缓存数据 no-store:所有内容都不会缓存。

协商缓存的方案

协商缓存需要进行对比判断是否可以使用缓存。浏览器第一次请求数据时,服务器会将缓存标识与数据一起响应给客户端,客户端将它们备份至缓存中。再次请求时,客户端会将缓存中的标识发送给服务器,服务器根据此标识判断。若未失效,返回304状态码,浏览器拿到此状态码就可以直接使用缓存数据了。

Last-Modified

Last-Modified: 服务器在响应请求时,会告诉浏览器资源的最后修改时间。

if-Modified-Since: 浏览器再次请求服务器的时候,请求头会包含此字段,后面跟着在缓存中获得的最后修改时间。服务端收到此请求头发现有if-Modified-Since,则与被请求资源的最后修改时间进行对比,如果一致则返回304和响应报文头,浏览器只需要从缓存中获取信息即可。 从字面上看,就是说:从某个时间节点算起,是否文件被修改了

  1. 如果真的被修改:那么开始传输响应一个整体,服务器返回:200 OK

  2. 如果没有被修改:那么只需传输响应header,服务器返回:304 Not Modified

 

Ps: 304,代表 NOT MODIFIED,他发生在这样的一种状态下:服务器正确接收到了一个带条件(Conditional Validation)的 GET,如果这个条件是真的就会返回 304、否则就会返回 200(A conditional GET or HEAD request has been received and would have resulted in a 200 OK response if it were not for the fact that the condition evaluated to false)。

换个角度来说,如果浏览器接收到的 response 的状态码是 304,就代表这个资源在客户端中的缓存依然是有效的,即在上次拿到资源到当前这段时间之内服务器端并没有对这个资源做修改。

这样做有什么好处呢? 显然这样可以在使用很小的一个 HTTP 请求的代价上就实现下面两个功能:

  • 确保拿到的资源是最新的

  • 客户端资源没有问题的情况下不需要再次那资源,解决每次完整请求资源时的性能问题。

 

if-Unmodified-Since: 从字面上看, 就是说: 从某个时间点算起, 是否文件没有被修改

  1. 如果没有被修改:则开始`继续'传送文件: 服务器返回: 200 OK

  2. 如果文件被修改:则不传输,服务器返回: 412 Precondition failed (预处理错误)

这两个的区别是一个是修改了才下载一个是没修改才下载。 Last-Modified 说好却也不是特别好,因为如果在服务器上,一个资源被修改了,但其实际内容根本没发生改变,会因为Last-Modified时间匹配不上而返回了整个实体给客户端(即使客户端缓存里有个一模一样的资源)。为了解决这个问题,HTTP1.1推出了Etag。

Etag

Etag: 服务器响应请求时,通过此字段告诉浏览器当前资源在服务器生成的唯一标识(生成规则由服务器决定)

If-None-Match: 再次请求服务器时,浏览器的请求报文头部会包含此字段,后面的值为在缓存中获取的标识。服务器接收到次报文后发现If-None-Match则与被请求资源的唯一标识进行对比。

  1. 不同,说明资源被改动过,则响应整个资源内容,返回状态码200。

  2. 相同,说明资源无须修改,则响应header,浏览器直接从缓存中获取数据信息。返回状态码304.

但是实际应用中由于Etag的计算是使用算法来得出的,而算法会占用服务端计算的资源,所有服务端的资源都是宝贵的,所以就很少使用Etag了。

 

优化

两类缓存机制可以同时存在,强制缓存的优先级高于协商缓存,当执行强制缓存时,如若缓存命中,则直接使用缓存数据库数据,不在进行缓存协商。

比较粗的说先后顺序应该是这样:

  1. Cache-Control —— 请求服务器之前

  2. Expires —— 请求服务器之前

  3. If-None-Match (Etag) —— 请求服务器

  4. If-Modified-Since (Last-Modified) —— 请求服务器

ps: 3和4一般验证一个就可以了

在此之前先了解缓存信息具体的获得途径

如果本地有相关资源的缓存,并且在缓存的时候响应头里面有 etag 或者 last-modified 的情况,这个时候去请求服务器的时候就会是带有条件的 GET 请求(Conditional Validation)。

在请求头里面可能会有两个字段: if-none-matchif-modified-since,其中 if-none-match 的值是服务器上次返回该资源时响应头里面 etag 的值,if-modified-since 的值是服务器上次返回该资源时响应头里面 last-modified 里的值。

紧接着服务器端就会接收到这个带有条件的 request,然后会根据这两个值去判断缓存的资源是否是最新的。

如果没问题,即资源是最新的情况下就会返回 304body 为空;不是的话就会返回 200,即目前浏览器端的资源不是最新的,body 里面就是资源体,然后客户端就会用最新返回的资源覆盖掉之前的资源

也就是说。发送这种带条件的请求的必要条件是 资源在浏览器端有缓存,并且在缓存的时候服务器端的 reponse 里面有 etag 或者 last-modified。如果这个条件不满足,发送的请求就是没有条件的(unconditionally)。

虽然这种方式能够减轻服务器的压力,解决一些请求资源时的性能问题。但是还是存在一些浪费:每个都要去带上条件请求服务器来看资源是不是最新的,于是服务器做了限制,在 response 里面加上 Cache-ControlExpires

通常他们是长这样的:

  • cache-control:max-age=96247433

  • expires:Thu, 03 Jan 2019 04:24:16 GMT

Cache-control 用于控制HTTP缓存(在HTTP/1.0中可能部分没实现,仅仅实现了Pragma: no-cache)Expires 表示存在时间,允许客户端在这个时间之前不去检查(发请求),等同 max-age 效果。但是如果同时存在,则被 Cache-Controlmax-age 覆盖。

 

各种途径的访问

浏览器输入 url 之后敲下回车就是先看本地 cache-control、expires 的情况

刷新(F5)就是忽略先看本地 cache-control、expires 的情况,带上条件 If-None-Match、If-Modified-Since

强制刷新(Ctrl + F5)就是不带条件的访问。

 

 

 

 

原文地址:https://www.cnblogs.com/wang-kai-1994/p/10431996.html