《HTTP权威指南》笔记:第七章-缓存

一、命中和未命中缓存

使用缓存的副本为到达缓存的请求提供服务被称为缓存命中(cache hit),如果没有已缓存的副本提供,而将请求转发给源服务器,被称之为缓存未命中(cache miss)

1.1 再验证

WEB缓存对已缓存的副本进行“新鲜度检测”,会向源服务器发送一个小的再验证请求,如果缓存仍然“新鲜有效”,那么服务器会返回一个304 not modified,然后缓存将副本提供给客户端。这被称为再验证命中或者缓慢命中

缓存一般会使用:(1)if-modify-since;(2)if-match;等HTTP首部进行再验证,会有以下结果:

  1. 再验证命中,服务端响应304
  2. 再验证未命中,服务端响应200并且带有完整内容。
  3. 对象被删除,服务端响应404,缓存清除该资源副本。

1.2 缓存命中率

由缓存提供服务所占的比例被称为缓存命中率,这个数值在0 ~ 1之间,对于现代中等规模以上的WEB应用来说,命中率保持在40%以上就达标了。

1.2 字节命中率

字节命中率直接体现对流量的节省,也能显著的降低时延,但是两者都是评价缓存性能的重要指标。

1.3 区分命中与未命中

检查HTTP报文的Date首部,如果时间早于请求时间,那么就是来自缓存,否则是来自服务端。

二、缓存的拓扑结构

缓存可以是单用户的(私有缓存),也可以是多用户共享的(公共缓存),下面是常见的缓存结构:

client -> private cache -> server
client -> public cache -> server
client -> private cache -> public cache -> server

2.1 私有缓存

Web浏览器中有内建的私有缓存。

2.2 公共代理缓存

公共缓存时特殊的共享代理服务器,被称为缓存代理服务器

2.3 代理缓存的层次结构

在代理缓存层次结构中,在靠近客户端的一级缓存中未命中的请求会被导向更高的父缓存,具体结构如下:

client/(private cache) -> first cache -> parent cache -> server

2.4 网状缓存、内容路由以及对等缓存

有些网络结构会哦固件负载的网状缓存(cache mesh),与缓存之间会以更加复杂的方式对话,可以做出动态缓存通信决策,决定与哪一个父缓存通信,或者直接与源服务器通信,也可将这种结构称为内容路由(content router),它具有以下功能:

  • 根据URLparent cacheserver之间做动态选择。
  • 根绝URL动态选择parent cache
  • 允许该结构其他缓存对缓存的副本进行访问,但是不允许外网流量访问。

三、缓存处理步骤

对一条HTTP GET报文的基本缓存处理步骤为:

  1. 接收。
  2. 解析,提取报文的URL以及headers
  3. 查询,在本地查询是否有已保存副本。
  4. 新鲜度检测。
  5. 创建报文,根据检测结果创建响应报文。
  6. 日志,可选的记录日志。

四、保持副本新鲜

4.1 过期日期和使用期

服务器使用HTTP/1.0+expires或者HTTP/1.1cache-control:max-age=value来规定缓存的使用时间区间。expires是绝对时间,依赖于客户端的始终,而cache-control使用的是相对时间,所以现在更倾向使用后者。

4.2 服务器再验证

如果已缓存的内容到期了(强缓存),那么会重新请求服务器对资源进行验证,如果未修改,返回304,更新缓存副本的首部;如果已修改,则返回200,然后丢弃原来的副本,缓存新的副本。

4.3 条件再验证

如果采取了协商缓存策略,那么可以发起条件GET,只有当条件满足了,服务端才会返回对象。HTTP定义了5个条件首部:

  1. if-modified-since:,如果从指定日期之后文档修改了,则从服务端返回新的对象(响应报文配合last-modified使用)。
  2. if-none-match:,如果tag与服务端指定资源的标记不同,则条件为true,返回新对象(响应报文配合etag:xxx使用)。
  3. if-unmodified-since:,在进行部分文件传输时,获取其他部分之前要确保文件未发生改变。
  4. if-range,针对不完整文档的缓存。
  5. if-match,与服务器通信时的并发控制。

4.3.1 if-none-match的必要性

  1. 有些文档会被周期性重写,尽管内容没改变,但是时间改变了。
  2. 有些文档的修改不重要,也许只是修改了注释之类的,则不需要重新获取。
  3. 有些服务器无法准确的判定最后修改日期。
  4. if-modified-since的时间粒度是秒,而有些文档可能在1秒内发生数次变化。

4.3.2 使用if-none-match和if-modified-since的时机

HTTP/1.0可能不能理解实体标签,所以服务端返回响应的时候最好同时返回last-modifiedetag这样可以使HTTP/1.0也能正常响应,如果服务端同时返回这两个首部,那么只有当这两个条件都满足,服务端才能返回304

五、如何控制缓存

按照优先级递减的顺序,服务端可以设置以下首部到响应报文中:

  • cache-control:no-store
  • cache-control:no-cache
  • cache-control:must-revalidate
  • cache-control:max-age
  • expires
  • 不设置相关头部,让缓存自己决定过期时间,可能是首部的date减去last-modified的五分之一(知乎上有人称之为启发式缓存)

5.1 缓存首部

  • cache-control: no-store,会阻止缓存对响应进行复制即不缓存。
  • cache-control:no-cache,可以被私有缓存复制保存在本地,但是在提供给客户端使用必须会与服务端进行再验证。
  • cache-control: max-age=100,缓存的活跃时间。
  • cache-control:s-maxage=100,针对公共缓存设置的缓存活跃时间。
  • expires:Fri,05 Jul 2020,05:00:00 GMT,绝对活跃时间,即在指定时间之前是活跃的。
  • cache-control: must-revalidate,如果不添加这个首部,有的缓存为了提高性能会给客户端提供过期的副本,添加这个首部告诉缓存在副本过期后必须重新请求,如果服务端不可用就必须返回504 gateway timeout

5.2 试探性过期

如果响应首部没有设置副本的过期时间,那么缓存会采取一定的算法计算出一个试探性的生存周期,比较常见的是LMF-Factor算法,具体计算公式为:lm_facotr * max(0, date - lastModified),一般lm_factor会取0.2。有的缓存保守起见会直接默认副本活跃时间为0,有的缓存则会设置最大值为1天或者1周。

5.3 客户端的新鲜度限制

当我们点击浏览器的刷新按钮重载页面时,一般来说浏览器会强制刷新缓存,客户端可以在请求首部加入cache-control以加强或者放松对过期时间的限制:

  • max-stale[=],如果只设置了max-stale表明客户端可以接受过期的缓存,如果设置了max-stale=100,就说明该缓存的副本在100s内不会过期,相当于是放松了对缓存过期时间的管控。
  • min-fresh=,要求在指定时间内是有效的。
  • max-age=,生命周期小于设置的值的副本就会被认为过期,相当于缩短了服务端设置的max-age
  • no-cache,强制缓存进行再验证。
  • no-store,强制缓存删除相关文档。
  • only-if-cached,只有当缓存中有该请求的副本,客户端才能获取到该对象(意思是必须从缓存取而不是服务器?)

参考文献

[1]《HTTP权威指南》

你可能感兴趣的:(《HTTP权威指南》笔记:第七章-缓存)