学习链接

谈一谈对浏览器的强缓存和协商缓存的理解

缓存(二)——浏览器缓存机制:强缓存、协商缓存

http面试必会的:强制缓存和协商缓存

彻底弄懂强缓存与协商缓存

浏览器缓存(强缓存和协商缓存)

这里说的缓存是指浏览器(客户端)在本地磁盘中对访问过的资源保存的副本文件。

浏览器缓存主要有以下几个优点:

  1. 减少重复数据请求,避免通过网络再次加载资源,节省流量。
  2. 降低服务器的压力,提升网站性能。
  3. 加快客户端加载网页的速度, 提升用户体验。

浏览器缓存分为强缓存协商缓存,区别:

  1. 如果浏览器命中强缓存,则不需要给服务器发请求
    而协商缓存会发送请求到服务器,并最终由服务器来决定是否使用缓存。

  2. Chrome 中强缓存(虽然没有发出真实的 http 请求)的请求状态码返回是 200 (from cache)
    而协商缓存如果命中走缓存的话,请求的状态码是 304 (Not Modified)
    不同浏览器的策略不同,在 Fire Fox中,from cache 状态码是 304.

from cache 会分为 from disk cache(磁盘缓存)from memory cache(内存缓存)

内存中获取最快,但是是 session 级别的缓存,关闭浏览器之后就没有了

请求流程

浏览器在第一次请求后缓存资源,再次请求时,会进行下面两个步骤:

  1. 浏览器会获取该缓存资源的 header 中的信息,根据 response header 中的 ExpiresCache-Control 来判断是否命中强缓存,如果命中则直接从缓存中获取资源。

  2. 如果没有命中强缓存,浏览器就会发送请求到服务器,这次请求会带上 If-Modified-Since 或者 If-None-Match,它们的值分别是第一次请求返回 Last-Modified或者 Etag,由服务器来对比这一对字段来判断是否命中协商缓存
    如果命中,则服务器返回 304 状态码,并且不会返回资源内容,浏览器会直接从缓存获取;
    否则服务器最终会返回资源的实际内容,并更新 header 中的相关缓存字段。

简要

  1. 先去内存看,如果有,直接加载
  2. 如果内存没有,择取硬盘获取,如果有直接加载
  3. 如果硬盘也没有,那么就进行网络请求
  4. 加载到的资源缓存到硬盘和内存

强缓存

强缓存是根据返回头中的 Expires 或者 Cache-Control 两个字段来控制的,都是表示资源的缓存有效时间

Cache-Control 的优先级高于 Expires

Expires

Expireshttp 1.0 的规范,值是一个 GMT 格式的时间点字符串。

这个时间点代表资源失效的时间,如果当前的时间戳在这个时间之前,则判定命中缓存。

失效时间是一个绝对时间,如果服务器时间与客户端时间偏差较大时,就会导致缓存混乱。

Expires 受限于本地时间,如果修改了本地时间,可能会造成缓存失效

Cache-Control

Cache-Controlhttp 1.1 的规范,一般常用该字段的 max-age 值来进行判断。

表示的是相对时间,比如 Cache-Control:max-age=3600 代表资源的有效期是 3600 秒。

并且返回头中的 Date 表示消息发送的时间,表示当前资源在 Date ~ Date + 3600s 这段时间里都是有效的。

Cache-Control 还可以设置其他值:

  • no-cache:不使用本地缓存(但可存数据到本地),需要通过协商缓存验证后才可使用
    强制客户端直接向服务器发送请求,也就是说每次请求都必须向服务器发送。
    这个很容易让人产生误解,使人误以为是响应不被缓存。实际上 Cache-Control: no-cache 是会被缓存的,只不过每次在向客户端(浏览器)提供响应数据时,缓存都要向服务器评估缓存响应的有效性。

  • no-store:直接禁止浏览器缓存数据,每次请求资源都会向服务器要完整的资源

  • public:可以被所有用户缓存(多用户共享),包括终端用户和 CDN 等中间件代理服务器

  • private:只能被终端浏览器缓存(而且是私有缓存),不允许中继缓存服务器进行缓存

协商缓存

协商缓存是由服务器来确定缓存资源是否可用。主要涉及到两对属性字段,都是成对出现的。

即第一次请求的响应头带上 Last-Modified 或者 Etag,则后续请求则会带上对应的请求字段 If-Modified-Since或者 If-None-Match

若响应头没有 Last-Modified 或者 Etag 字段,则请求头也不会有对应的字段。

ETag的优先级高于Last-Modified

Last-Modified,If-Modified-Since

Last-Modified 表示当前文件的最后修改时间,值为 GMT 格式的时间字符串。(http 1.0

下一次请求时,请求头中会带上 If-Modified-Since(值为上次的 Last-Modified ),询问服务器在该日期后是否有更新,服务器根据文件的最后修改时间判断资源是否有变化。

如果文件没有变更则返回 304 状态码,浏览器直接使用本地缓存,否则返回新资源并更新 Last-Modefied 的值。

(服务器返回 304 时,响应头中不会返回 Last-Modified

ETag,If-None-Match

ETag 表示当前资源的唯一标识串,只要资源有变化就这个值就会改变,与最后修改时间无关。(http 1.1

下一次请求时,请求头中会带上 If-None-Match(值为上次的 ETag),询问服务器该资源是否有变化,服务器根据文件本身算出资源的唯一标识串来判断资源是否有变化。

服务器根据文件本身算出一个哈希值并通过 ETag字段返回给浏览器,接收到 If-None-Match 字段以后,服务器通过比较两者是否一致来判定文件内容是否被改变。

如果文件没有变更则返回 304 状态码,浏览器直接使用本地缓存,否则返回新资源并更新 ETag 的值。

(服务器返回 304 时,响应头中会返回重新计算过的 ETag,即使这个 ETag 跟之前的相同)

HTTP 中并没有指定如何生成 ETag,可以由开发者自行生成,哈希是比较理想的选择。

为什么要有 ETag

HTTP1.1ETag 的出现主要是为了解决几个 Last-Modified 比较难解决的问题

  • 一些文件也许会周期性的更改,但是内容并不改变(仅仅改变的修改时间),这个时候我们并不希望客户端认为这个文件被修改了,而重新 GET
  • 某些文件修改非常频繁,比如在秒以下的时间内进行修改,(比方说 1s 内修改了 N 次),If-Modified-Since 能检查到的粒度是秒级的,使用 Etag 就能够保证这种需求下客户端在1秒内能刷新 N 次 Cache。
  • 某些服务器不能精确的得到文件的最后修改时间

优先级

  1. Cache-Control > Expires > ETag > Last-Modified

缓存的选择

大部分 web 服务器都默认开启协商缓存,而且是同时启用Last-Modified/If-Modified-SinceETag/If-None-Match

但是下面的场景需要注意:

  • 分布式系统里多台机器间文件的 Last-Modified 必须保持一致,以免负载均衡到不同机器导致比对失败;
  • 分布式系统尽量关闭掉 ETag(每台机器生成的 ETag 都会不一样)