HTTP缓存机制

在开始介绍HTTP缓存机制之前,先介绍一下代理服务器等几个相关概念以便理解。

代理服务器

代理服务器是介于客户端(如浏览器)与服务器之间的一台服务器。
代理服务器的基本行为就是接收客户端发送的请求后转发给其他目标服务器,从目标服务器返回的响应经过代理服务器后再传给客户端。
代理服务器避免了客户端与服务器的直接连接,有利于保护终端的隐私或安全,突破访问限制,利用缓存减少通信流量和访问时间,等等。

缓存服务器

缓存服务器是代理服务器的一种,当代理转发从服务器返回的响应时,代理服务器将会保存一份资源的副本。当代理再次接收到对相同资源的请求时,就可以不从目标服务器那获取资源,而是将之前缓存的资源作为响应返回。

Web 缓存

缓存指的是将需要频繁访问的网络内容存放在离用户较近、访问速度更快的系统中,以提高内容访问速度的一种技术。缓存服务器就是存放频繁访问内容的服务器。

缓存的种类

缓存的种类分为多种:

  • 本地缓存(如浏览器缓存):将Web对象缓存的拷贝保存在本地计算机中。大多数流行的Web浏览器默认情况下保留一个先前访问对象的缓存。例如,Internet Explorer称之为“临时Internet文件”。本地缓存拷贝只是在用户频繁地从同一台机器访问页面时有用。它直接从本地获取资源,减少了网络流量,加快了请求速度。

  • 数据库缓存:数据库的工作模式按存储方式可分为 硬盘数据库 和 内存数据库,内存数据库在读取数据时不会受到硬盘 I/O 的影响,速度更快

  • 代理服务器缓存:代理服务器是为公司内的多个用户/客户计算机缓存Web对象的单独机器。它们是位于客户端和托管的Web服务器之间的计算机,而且它们比本地缓存效率更高,因为在企业本地网络中的任何用户或计算机访问某个Web对象时,缓存拷贝对想访问该对象的任何其他用户/计算机是可用的,无需到Internet服务器上再次下载它。代理缓存可以在网络边缘与防火墙结合使用。

  • CDN缓存:CDN缓存是缓存服务器的一种,它的全称是Content Delivery Network,即内容分发网络。其基本思路是尽可能避开互联网上有可能影响数据传输速度和稳定性的瓶颈和环节,使内容传输的更快、更稳定。通过在网络各处放置反向代理节点服务器所构成的在现有的互联网基础之上的一层智能虚拟网络,CDN系统能够实时地根据网络流量和各节点的连接、负载状况以及到用户的距离和响应时间等综合信息将用户的请求重新导向离用户最近的服务节点上。其目的是使用户可就近取得所需内容,解决 Internet网络拥挤的状况,提高用户访问网站的响应速度。
    扩展阅读:http://www.cnblogs.com/rayray/p/3553696.html

浏览器缓存机制

1. 存储缓存

客户端第一次向服务端请求资源之后,服务器会将资源和该资源的缓存规则一起返回。缓存规则是被放在 HTTP 响应的头信息中的,缓存规则包括客户端是否需要缓存数据,缓存数据的有效时长等。
浏览器第一次发起请求:

通过配置 HTTP 响应头中的 Cache-Control,可以指明响应内容是否可以被客户端存储。
Cache-Control 的值包含有以下几个常用消息中的指令:

  • Public 指示响应可被所有用户缓存,包括客户端用户和代理服务器。
  • Private 指示响应只能被特定用户缓存,即仅客户端可以缓存,代理服务器不可缓存。
  • no-cache 强制所有缓存了该响应的用户,在使用已经存储的缓存前,需要发送带验证的请求到服务器
  • no-store 在请求消息中发送将使得请求和响应消息都不使用缓存,完全不存下來。
  • max-age 指示客户机可以接收生存期不大于指定时间(以秒为单位)的响应。上次缓存时间(客户端的)+max-age(64200s)<客户端当前时间
  • min-fresh 指示客户机可以接收响应时间小于当前时间加上指定时间的响应。
  • max-stale 指示客户机可以接收超出超时期间的响应消息。如果指定 max-stale 消息的值,那么客户机可以接收超出超时期指定值之内的响应消息。

这些由服务端返回的资源数据以及相应的响应头信息(缓存规则)将会被浏览器保存到本地。
(在win10中,Chrome浏览器的缓存文件默认保存在 C:Users{用户名}AppDataLocalGoogleChromeUser DataDefaultCache 目录下,通过 ChromeCacheView 这个工具可以查看该目录下的缓存文件)。

2. 浏览器缓存的类型

当浏览器再次发起请求时,假如已经存在本地缓存,浏览器就会检查请求头中是否设置了 Cache-Control:no-cache 和 Cache-Control:max-age=0 (强制向服务器端发送请求),如果不存在,就会读取缓存文件中保存缓存规则,根据保存在缓存文件中的缓存规则,浏览器将缓存分为强缓存协商缓存两种类型:
浏览器在请求加载某资源时,会先根据对这个资源的 http 请求中的 header 信息,来判断对该资源的请求是否符合强缓存规则,如果符合,则直接读取本地缓存,不会向服务器发起请求。
如果不符合强缓存规则时,浏览器就会发送请求到服务器端,然后服务端会接着验证该请求是否符合协商缓存的规则,如果符合协商缓存,则服务端返回 304 响应,但不会返回这个资源数据,304 表示通知客户端仍可以使用之前的缓存,而如果不符合协商缓存,则服务端将资源返回给客户端(200),并更新本地缓存数据。
可见强缓存的优先级高于协商缓存,当强缓存生效时,就不会执行验证协商缓存了。

强缓存

强缓存其实就是客户端判断本地缓存是否已过期,如果未过期,则直接读取本地的缓存数据,否则需要从服务器获取。
强缓存由缓存文件中保存的 Expires 和 Cache-Control 来控制的:

  • Expires:指明该缓存资源的过期时间,它描述的是一个绝对时间,由服务器端返回 ,如:FRI 31 Dec 2008 09:07:08 GMT。
    当客户端的时间超过这个绝对时间后,该缓存数据就失效了。
    缺点:假如客户端与服务器端存在时差(比如时钟不同步,跨时区等),那么就会产生较大的误差,所以从 HTTP 1.1 版本开始,Expires 被 Cache-Contro:max-age 取代了。

  • Cache-Control:max-age,它描述的是一个相对时间。当 客户端的当前时间 大于 客户端缓存数据时的时间 + max-age ,则该缓存数据失效。例如:

该响应的 Cache-Control:max-age = 31536000 指明了这个资源可以被缓存 31536000 秒(365天),也就是说在 365 天之内请求该资源,都会直接从缓存文件中读取。

协商缓存

即使缓存文件由 Cache-Control:max-age 标记为过期,也不代表该缓存从此就失去作用了。客户端会将请求发送到服务端来进一步判断该缓存是否仍然有效。
一,Last-Modified/if-Modified-Since

  • Last-Modified : 指明资源最终修改的时间,由服务端返回的响应头字段,保存在缓存文件中。
  • if-Modified-Since:它的值为上次响应中返回的 Last-Modified 的值;
    当资源过期时(强缓存失效),浏览器发现该缓存资源中含有 Last-Modified 资源,就会将该 Last-Modified 的值取出,并赋值给请求头中的 if-Modified-Since 字段,但服务前收到了包含有 if-Modified-Since 的请求后,就会与被请求的资源的最后修改时间进行对比。

如果服务器资源最后修改的时间 大于 if-Modified-Since 字段中指定的时间,则表示该资源发生过更新,服务端会返回 200、新的资源以及新资源的 Last-Modified。

如果服务器端资源的最后修改时间 小于或等于 if-Modified-Since 中的时间,则说明该资源没有被更新过,服务端返回 304,告知浏览器继续使用缓存文件。

缺点:

  • Last-Modified 指明的最后修改时间只能精确到秒级,它将无法判断1s内的频繁修改。
  • 如果某些文件会被周期性的更新,它的内容不变,仅仅改变修改时间,此时我们也不希望客户端认为这个文件被修改了而重新请求。

于是在 HTTP 1.1 中又出现了 Etag。

二,Etag/If-None-Match(优先级高于 Last-Modified/If-Modified-Since)

  • Etag:当客户端第一次发起请求之后,服务端会返回当前请求的资源在服务端的唯一标识 Etag(Etag 的生存规则由服务器决定,Aapche中,Etag 默认是对文件的索引节 INode,大小Size 和 最后修改时间进行 Hash 之后得到的)。
  • If-None-Match:当客户端再次请求该资源时,将会去取出该资源缓存文件的唯一标识符 Etag 的值放入请求的 If-None-Match 字段中。
    服务器收到请求头中的 If-None-Match 之后,将会与该资源的唯一标识符进行对比,
    如果不相同,则表示资源被更新过,返回 200;
    如果相同,则表示该资源为被更新过,返回 304。

Last-Modified 与 Etag 可以同时使用,但服务器会优先检验 Etag,如果 Etag相同,才会接着验证 Last-Modified。

所以总的来说,浏览器处理缓存的流程大概是这样的:

  1. 第一次发起请求时,判断是否需要对响应数据进行缓存
  2. 再次发起请求时,查询是否存在缓存。
  3. 如果存在缓存,而且缓存未过期,则直接使用本地缓存(强缓存)
  4. 如果缓存已过期,则向服务端发起请求,验证本地缓存是否仍可用(协商缓存)

浏览器再次发起请求时的流程图:

参考:
《图解HTTP》
https://blog.csdn.net/qq_26517369/article/details/78330694
http://www.cnblogs.com/chenqf/p/6386163.html
https://mp.weixin.qq.com/s/uWPls0qrqJKHkHfNLmaenQ
https://blog.csdn.net/budapest/article/details/79376505

原文地址:https://www.cnblogs.com/liyutian/p/9574877.html