https://juejin.im/post/5c6e77da6fb9a049db73bb07

地址栏缓存

我们使用一个常用的网址时,只是输入了几个字母,浏览器就自动补全了该网址。这个对应页面扽相关资源也是从缓存中读取的。

检查 HSTS 预加载列表

HSTS( HTTP Strict Transport Security )国际互联网工程组织 IETE 正在推行一种新的 Web 安全协议,作用是强制客户端(如浏览器)使用 HTTPS 与服务器创建连接。

支持这个协议的浏览器,在输入 URL 后会检查自带的 HSTS 预加载列表。这个列表里包含了那些请求浏览器只使用 HTTPS 进行连接的域名。
若网站在这个列表里,浏览器会使用 HTTPS 协议并且返回码为 307。而不支持 HSTS 的浏览器访问我们的网站,则不会产生跳转,从而提高了兼容性。

DNS 缓存

域名解析最少涉及了三个地方的缓存:

  • 浏览器的 DNS 缓存
  • 操作系统中的 DNS 缓存
  • 操作系统的 hosts 文件

域名解析的具体过程

image.png

  1. 浏览器搜索自己的 DNS 缓存(浏览器维护一张域名与 IP 地址的对应表);如果没有命中,进入下一步;
  2. 搜索操作系统中的 DNS 缓存;如果没有命中,进入下一步;
  3. 搜索操作系统的 hosts 文件( Windows 环境下,维护一张域名与 IP 地址的对应表);如果没有命中,进入下一步;

然后

  1. 操作系统将域名发送至 LDNS (本地区域名服务器),LDNS 查询自己的 DNS 缓存(一般命中率在 80% 左右),查找成功则返回结果,失败则发起一个迭代 DNS 解析请求:
  2. LDNS向 Root Name Server(根域名服务器,如com、net、org等的顶级域名服务器的地址)发起请求,此处,Root Name Server 返回 com 域的顶级域名服务器的地址;
  3. LDNS 向 com 域的顶级域名服务器发起请求,返回 cnblogs.com 域名服务器地址;
  4. LDNS 向 cnblogs.com 域名服务器发起请求,得到 cnblogs.com 的 IP 地址;
  5. LDNS 将得到的 IP 地址返回给操作系统,同时自己也将 IP 地址缓存起来;操作系统将 IP 地址返回给浏览器,同时自己也将 IP 地址缓存起来。

DNS Prefetch

即 DNS 预获取,是前端优化的一部分。一般来说,在前端优化中与 DNS 有关的有两点:

  • 减少 DNS 的请求次数
  • 进行 DNS 预获取

典型的一次 DNS 解析需要耗费 20-120 毫秒,减少DNS解析时间和次数是个很好的优化方式。DNS Prefetching 是让具有此属性的域名不需要用户点击链接就在后台解析,而域名解析和内容载入是串行的网络操作。这个方式能减少用户的等待时间,提升用户体验。

浏览器 DNS 缓存的时间一般不会太长,一分钟左右。为什么缓存不设置较长时间呢? 虽然 DNS 缓存可以提高获取 DNS 的速度,但缓存时间过长也会影响 DNS 在 IP 变更时不能及时解析到最新的 IP。

ARP(地址解析协议)缓存

ARP(Address Resolution Protocol),ARP 是一种用以解释地址的协议,根据通信方的 IP 地址就可以反查出对应方的 MAC 地址的一个TCP/IP协议。

ARP 缓存是个用来储存 IP 地址和 MAC 地址的缓冲区,其本质就是一个 IP 地址与 MAC 地址的对应表,表中每一个条目分别记录了其他主机的 IP 地址和对应的 MAC 地址。

当地址解析协议被询问一个已知 IP 地址节点的 MAC 地址时,先在 AR 缓存中查看,若存在,就直接返回与之对应的MAC地址;若不存在,才发送 ARP 请求查询。

TCP 发送缓冲区 & 接收缓冲区

建立 TCP 连接这一步也涉及到缓存 —— 用来临时存放双方通信的数据,保证通信数据不会丢包。
每个 TCP 连接在内核中都有一个发送缓冲区和接收缓冲区,TCP 的全双工的工作模式以及 TCP 的流量(拥塞)控制便是依赖于这两个独立的 buffer 以及 buffer 的填充状态。

HTTP 请求缓存(CDN 节点缓存、代理服务器缓存、浏览器缓存、后端动态计算结果缓存等)

强缓存 ( Cache-Control 和 Expires )

强缓存主要是采用响应头中的 Cache-Control 和 Expires 两个字段进行控制的。Expires 是 HTTP 1.0 中定义的,它指定了一个绝对的过期时期。Cache-Control 是 HTTP 1.1 时出现的缓存控制字段。

Expires 是 HTTP1.0 时代的产物,设计之初就存在着一些缺陷,如果本地时间和服务器时间相差太大,就会导致缓存错乱。Cache-Control 的优先级会更高一点。

这两个字段的效果是类似的,客户端都会通过对比本地时间和服务器返回的生存时间来检测缓存是否可用。如果缓存没有超出它的生存时间,客户端就会直接采用本地的缓存。如果生存日期已经过了,这个缓存也就宣告失效。接着客户端将再次与服务器进行通信来验证这个缓存是否需要更新。

请求头 Cache-Control

指令 说明
no-cache 使用代理服务器的缓存之前提交原始服务器验证,验证通过才能使用
no-store 在客户端或是代理服务器都不缓存请求或响应的任何内容
max-age=[秒] 告知服务器客户端可接受资源的存在最大时间
max-stale(=[秒]) 可接受(代理服务器缓存的)过期资源,参数可省略
min-fresh=[秒] 可接受(代理服务器缓存的)资源更新时间小于指定时间
no-transform 代理服务器不可以更改媒体类型
only-if-cached 客户端只接受已缓存的响应,若缓存不命中,则返回 504 错误
cache-extension 自定义扩展值,若服务器不知别该指令,就直接忽略

响应头 Cache-Control

指令 说明
public 表明该资源可以给多个用户使用
private(= name) 该资源是私有资源,指定的用户可以使用的缓存
no-cache 强制每次请求直接发送给源服务器,而不经过本地缓存版本的校验。
no-store 在客户端或是代理服务器都不缓存请求或响应的任何内容
no-transform 代理服务器不可以更改媒体类型
must-revalidate 可缓存但必须再向源服务器进行请求确认
proxy-revalidate 要求缓存服务器返回缓存的时候向源服务器进行请求确认
max-age=[秒] 告知客户端该资源在规定时间内是新鲜的,无需向服务器确认
s-maxage=[秒] 告知缓存服务该资源在规定时间内是新鲜的,无需向服务器确认
cache-extension 自定义扩展值,若服务器不识别该指令,就直接忽略

参看:https://zhuanlan.zhihu.com/p/60357719 可能是最被误用的 HTTP 响应头之一 Cache-Control: must-revalidate

可缓存性

  1. public:响应可以被任何对象(客户端、代理服务器等)缓存
  2. private:只能被单个用户缓存,不能作为共享缓存
  3. no-cache:使用缓存副本之前,需要将请求提交给原始服务器进行验证,验证通过才可以使用
  4. only-if-cached:客户端只接受已缓存的响应,并且不向原始服务器检查是否有更新的拷贝

到期

  1. max-age=<seconds>:缓存存储的最大周期,超过这个时间缓存被认为过期(单位秒)。与 Expires 相反,时间是相对于请求的时间
  2. s-maxage=<seconds>:覆盖 max-age 或者 Expires 头,但是仅适用于共享缓存(比如各个代理),并且私有缓存中它被忽略
  3. max-stale[=<seconds>]:表明客户端愿意接收一个已经过期的资源。可选的设置一个时间(单位秒),表示响应不能超过的过时时间
  4. min-fresh=<seconds>:表示客户端希望在指定的时间内获取最新的响应

重新验证和重新加载

  1. must-revalidate:缓存必须在使用之前验证旧资源的状态,并且不可使用过期资源。
  2. proxy-revalidate:与 must-revalidate 作用相同,但它仅适用于共享缓存(例如代理),并被私有缓存忽略

其他

  1. no-store:彻底得禁用缓冲,本地和代理服务器都不缓冲,每次都从服务器获取
  2. no-transform:不得对资源进行转换或转变。Content-Encoding, Content-Range, Content-TypeHTTP 头不能由代理修改。

协商缓存 ( Last-Modified 和 Etag )

协商缓存机制下,浏览器需要向服务器去询问缓存的相关信息,进而判断是重新发起请求还是从本地获取缓存的资源。如果服务端提示缓存资源未改动( Not Modified ),资源会被重定向到浏览器缓存,这种情况下网络请求对应的状态码是 304。

Last-Modified 和 If-Modified-Since

这是基于资源在服务器修改时间而验证缓存的过期机制。
当客户端再次请求该资源的时候,会在其请求头上附带上 If-Modified-Since 字段(值就是第一次获取请求资源时响应头中返回的 Last-Modified 值)。如果修改时间未改变则表明资源未过期,命中缓存,服务器就直接返回 304 状态码,客户端直接使用本地的资源。否则,服务器重新发送响应资源,从而保证资源的有效性。

Etag 和 If-None-Match

这是基于资源校验码(一般为md5值)而验证缓存的过期机制。
当客户端再次请求该资源的时候,会在其请求头上附带上 If-None-Match 字段(值就是第一次获取请求资源时响应头中返回的 Etag 值),其值与服务器端资源文件的验证码进行对比,如果匹配成功直接返回 304 状态码,从浏览器本地缓存取资源文件。如果不匹配,服务器会把新的验证码放在请求头的 Etag 字段中,并且以 200 状态码返回资源。
当响应头中同时存在 Etag 和 Last-Modified 的时候,会先对 Etag 进行比对,随后才是 Last-Modified。

Etag 的问题

相同的资源,在两台服务器产生的 Etag 是不是相同的,所以对于使用服务器集群来处理请求的网站来说,Etag 的匹配概率会大幅降低。所在在这种情况下,使用 Etag 来处理缓存,反而会有更大的开销。