HTTP-缓存机制

这是我阅读《HTTP权威指南》的总结和思考中,一系列文章的一篇,目录在这里

为什么需要缓存

缓存主要解决了四个问题

  1. 冗余数据传输: 节约了网络费用
  2. 带宽瓶颈: 降低了传输时延(=文件大小/带宽)
  3. 瞬间拥塞: 对于一些突发时间, 降低了对服务器的要求, 避免了网络崩溃
  4. 距离时延: 降低了传播时延(距离/光纤速度)

缓存结构

缓存分为两种:

  1. 私有缓存: 浏览器的临时文件
  2. 公有缓存: 特殊的代理服务器

拓扑结构又有两种:

  1. 层级结构: 越高层次缓存越大, 服务的用户越多, 私有缓存可以认为是第一级缓存
  2. 网状缓存/对等缓存: 多个不同组织之间共用缓存, 可以查询对等组织的缓存

缓存处理步骤

对于一个代理缓存服务器, 其处理缓存主要由7步构成, 核心在于新鲜度检测与更新

接收、解析、查找

  1. 缓存服务器监测到客户端输入,读取数据,在全部报文抵达前开始处理
  2. 将请求报文分片解析,格式化为易于分析的数据结构
  3. 在自身的存储(内存或者磁盘)中快速寻找该资源副本,如果不存在就访问服务器申请

新鲜度检测

新鲜度检测是缓存处理的核心,如果一个资源的副本不够新鲜,就要根据一定的策略向服务器查询该副本是否变化

文档过期

  1. Cache-Control(HTTP/1.1): max-age: 定义一个以秒为最大单位的文档使用期限

    1
    Cache-Control: max-age=484200
  2. Expires(HTTP/1.0+): 定义一个过期日期,依靠对本机日期(需要精确)和改日期比较确定是否过期

    1
    Expires: Fri, 05 Jul 2002, 05:00:00 GMT

再验证

如果同时携带两者,必须同时满足才可以发送304

  1. If-Modified-Since: :服务器通过比较文件的修改日期和该头部的日期,确定是返回304还是200+文件实体

    1
    If-Modified-Since: Sat, 29 Jun 2002, 14:30:00 GMT
  2. If-None-Match: :因为只用时间可能: 秒不够精确、未发生实际修改、服务器无法判断修改日期。所以资源返回一个ETag标签来标示是否修改

    1
    2
    3
    4
    5
    6
    //Request
    If-None-Match: "v2.6"
    //Response
    304 Not Modified
    ETag: "v2.6"
    Expires: ......(date)

响应、发送、日志

  1. 缓存创建响应,其中Date首部使用原始服务器的值让客户端知道这是一个缓存
  2. 发送创建的响应
  3. 缓存服务器记录与缓存相关数据,如命中率等

缓存控制策略

控制策略

服务器通过Cache-ControlExpires首部来控制客户端的缓存策略

  1. no-Store: 不允许存储本地缓存文件,每次从服务器下载
  2. no-Cache: 允许存储缓存,但是每次都必须与原是服务器进行再验证
  3. max-age: 之前讨论过,表示新鲜度维持时间,=0类似于no-Cache
  4. must-revalidate: 有些缓存允许返回过期资源副本,设置该头部后,缓存只允许提供新鲜的副本,过期副本必须验证
  5. Expires: 之前讨论过,依赖于服务器时间,不推荐使用

试探新鲜度

如果没有设置新鲜度(max-age或Expires):

通常使用一些试探算法进行猜测,如LM算法

1
新鲜度 = LM因子(比如0.2) * (资源获取时间 - 资源最后修改时间)

如果无法获得最后修改日期,就只能直接设置为1小时或一天了

客户端新鲜度控制

客户端也可以通过Cache-Control请求来强化或者放松对过期时间的限制

  1. Cache-Control: max-stale (=<s>) 缓存可以提供过期文件,如果有参数s,最多过期s时间
  2. Cache-Control: min-fresh = <s> 缓存在未来s秒内要保持新鲜
  3. Cache-Control: max-age = <s> 缓存无法返回缓存时间大于s的资源
  4. no-Cache or no-Store 必须再验证 or 必须尽快删除存储
  5. only-if-cached 只有缓存存在副本,才获取一份副本