那些年被忽略的——HTTP缓存的过期模型

161 阅读4分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第20天,点击查看活动详情

当缓存有效时,可以减少客户端对源服务器的请求。

服务器指定过期

  • 服务器给响应显式的将某个时间设置为过期的时间点
    • 当这个时间是将来的某个时间,并且确认在过期时间之前实体不会改变。这样的响应可用于缓存的响应,判断响应是否过期,但是不能转发给客户端响应。
    • 当这个时间是过去的某个时间表示源服务器希望强制语义透明的缓存去验证每个请求。也就代表响应是陈旧的,所以当缓存利用这个响应去服务后续的请求时,缓存应该验证这个响应。
  • 源服务器可以利用must-revalidate缓存控制指令强迫http/1.1的缓存去验证每个请求
  • 服务器指定显式过期时间是利用Expires头部或Cache-control头部里的max-age缓存控制指令
  • 过期时间不能被用于强制代理去刷新新是或重载资源。过期的语义值应用于缓存下的机制。当对资源发起新请求时,只能检测请求资源的过期状态

自发过期

  • 除了源服务器提供过期时间外,http缓存还会利用其他头部的值(比如Last-Modifified)通过一种算法去估计一个合理的过期时间。规范没有提供特定的算法,但是加强了因为算法可能出现的最坏情况的限制。

寿命计算

  • 为了知道缓存项是否是最新的,缓存需要知道缓存项的寿命是否保持最新
  • http/1.1要求源服务器尽可能在每个响应里附加一个Date头部,值为响应产生的时间,当从缓存里获取响应消息的时候,利用Age头部来表示响应消息从源服务器产生或重验证到现在的时间。基于现实情况,Age的值是响应从源服务器经过每个缓存停留的时间的总和,外加响应在网络的传输时间
  • Age头部出现在相应里说明响应是缓存的,但是没有Age头部的响应,不一定是直接来自源服务器的,因为http之前的版本中没有定义Age头部

过期计算

  • 为了确定响应是新的还是旧的,通过比较响应的保鲜时间和age进行比较
  • 过期计算中有两个保鲜时间,一个是来自Expires头部的值,一个是Cache-Control里max-age的值,max-age指令优先于Expires头部
  • 如果响应中没有出现出现时间,并且没有包含其他的控制,那么可以自发的计算保鲜时间

消除歧义的过期值

  • 由于过期值可能会被任意设置,可能出现两个缓存包含同一资源但是Date的值不一样
    • 如果缓存项里的Date头部比新响应的Date值新,客户端应该忽略新响应
  • 如果一个缓存有两个没有过期的响应和不同的验证器,那么缓存会使用Date值是最近的响应

消除多个响应的歧义

  • 客户端可能收到经过不同路径的响应,客户端收到的响应顺序可能和源服务器发送的响应的顺序不一样。客户端应该使用最新的响应,或者是没有过期的旧响应
  • 因为Date的精度只有秒级,所以响应的顺序很难决定,需要客户端去重验证
  • 验证新的缓存项的Date比已存在的缓存项的Date晚,客户端无条件的重试请求,并且包含头部:
    • Cache-Control:max-age=0强制任何中间缓存通过服务器去验证他们的缓存副本
    • 或者Cache-Control:no-cache强制任何中间缓存从源服务器获得一个新副本