先记住这个答案
计算当前 Age 时,先得到 apparent_age = max(0, response_time - date_value),再以 corrected_age_value = Age 头值 + response_delay 做校正,取二者较大值作为初始年龄,加上本缓存驻留时间即得当前 Age。多级代理中 Date 保持不变,每跳都按这段逻辑重新计算 Age 并透传,从而补偿传输延迟和本地停留。
- apparent_age 是响应时间与 Date 的差
- 每级代理都要加 response_delay 校正
- Age 在多级缓存中逐跳累加且 Date 不改
RFC 9111 中 age_value 的两步校正机制
缓存收到响应时先记录 request_time 与 response_time。第一步算 apparent_age,它是响应时刻减 Date 的值;由于 Date 表示源站生成时刻,这个差说明了响应已经存在多久。若当前节点时钟比源站慢,差的负会被钳为 0,所以用 max(0, ...) 规避时钟差异造成负年龄。
第二步看 Age 头携带的年龄以及本段传输耗时。corrected_age_value = Age 头数值 + (response_time - request_time),这是假设上游 Age 是相对真实时间估算出的,再补齐本段中间的转发延迟和排队。最终 corrected_initial_age = max(apparent_age, corrected_age_value);此后只要缓存未取出使用,年龄随当前时间继续增加。
两端子级联传输的具体计算
设源站 Date 为 12:00:00,第一级代理 C1 的 request_time=12:00:05、response_time=12:00:10。C1 的 apparent_age=10 秒,Age 头为空,response_delay=5 秒,故 corrected_age_value=5,取大者初始年龄=10,且 C1 在 12:00:10 存储后到 12:00:15 被下一级请求,resident_time=5,因此转发时 Age 头应写 15。
客户端后接的 C2 在 12:00:15 发出请求,收到响应时是 12:00:18,Date 仍是 12:00:00。C2 先取 Age 头 15,计算 response_delay=3,corrected_age_value=18;而 apparent_age=18,max(18,18) 得初始年龄 18,不久后给浏览器用时 Age 为 18。路径上总延迟 8 秒被两跳的本地驻留和传输正确反映,而不是只依赖源站 Date。
时钟偏移和传输欠载下的失效边界
若中间某级机器时钟明显快于源站,apparent_age 会被放大,可能超过真实年龄,而 max() 无法排除偏快程度,导致 Age 高估;若时钟慢,则 apparent_age 被钳为 0,可能低估 Age。corrected_age_value 依赖上游 Age 头,但当 apparent_age 更大时仍会被采用,因此最终 Age 可能失真,需依赖 NTP 同步减小误差。若上游 Age 头被清空,则失去该参考依据。
另一个边界是重验证场景。当缓存持有陈旧响应发起验证并收到 304 时,Date 仍是源站原始时间,而 response_time 是新时间,apparent_age 会包含缓存驻留时长,但 response_delay 仅反映本段验证请求的延迟,因此 corrected_age_value 可能低于真实年龄。RFC 9111 的 4.3.4 节规定了验证后的处理,并非简单将 Age 重置为 0,需依据响应头更新存储,避免陈旧 Age 误叠加。
容易答错的地方
- 只把 Date 与当前时间差当 Age
- 即使 Date 是源站生成时间,直接从 Date 推导出的 apparent_age 忽略了代理间的转发耗时。若不加上 response_delay 并参考上游 Age,多级链路会低估实际年龄,导致本已过期的响应被当作新鲜发送。正确做法是取 apparent_age 和 age_value+response_delay 中较大者。
- 每级缓存的 Date 都重写为本地时间
- Date 头在转发时必须保留源站原始值,只能通过 Age 头表示增量。若改动 Date,各入口计算 apparent_age 会直接错乱,之前的 Age 也失去基准。每级只应修改 Age,除特定兼容处理外不得改写 Date。
面试官还会怎么问?
如果中间某级缓存与源站时钟差异超过十秒,Age 还可靠吗?
reliability 依赖上游 Age 和 response_delay 的补偿。时钟差异过大时 apparent_age 失真,但 corrected_age_value 不受影响,仍可作为备选。只有同时缺少 Age 头且 response_delay 很小才会可能明显误判;生产上建议各层保持 NTP 同步并显式携带 Age。
Age 超过响应新鲜度后,缓存是否一定回源?
不一定。Age 只表示响应存在时间,新鲜度判断还需看本地请求的 max-age 等指令;若允许 stale-while-revalidate 或 max-stale,缓存仍可先用旧响应并后台验证。Age 越大越可能触发回源,但并非唯一判据。
Age 头由源站生成后在多个代理中透传,接收方需要重新累加吗?
每个缓存或代理在转发前都必须按自身记录位置重新计算 Age,而不能简单透传。即便上游 Age 正确,本段驻留时间也必须靠 response_time 回推补入,否则最终消费者看到的 age 过小。
参考资料
示例用于理解所注明的运行环境与边界;延伸学习可结合原文中的更多案例。