一文读懂HTTP Caching

一个典型的HTTP应用拓扑如下

user-agent — forward proxy … proxy server n … reverse proxy — origin server

user-agent通常是浏览器,也可以是第三方工具如curl,或HTTP相关模块,如python的request库。
forward proxy是正向代理,逻辑上靠近客户端,可以在浏览器的设置中指定。
proxy server n是处于中间位置,提供HTTP代理功能的代理服务器。
reverse proxy是反向代理,位于origin server服务端的边缘,由它代理提供WEB服务。
origin server真实提供WEB服务的源WEB服务器。

HTTP Cache是一个本地存储,保存了可缓存的响应消息。它可以分为private cache和public cache。比如浏览器上的cache就是一个private cache,仅为一个终端用户服务;而share cache就是为多个用户提供缓存服务的cache服务器,它可以在user-agent到origin server上的任何proxy上实现。

HTTP Cache的目的是降低WEB服务响应的时延,减少带宽消耗。它的基本工作逻辑是,保存上一次用户请求的结果到HTTP cache,以后有相同请求发往origin server时,则直接从cache中返回结果,比如响应消息直接从浏览器本地缓存中取,显然响应更快,而且没有带宽消耗。当然上述只是理想情况,具体到现实世界,还有很多因素需要考虑。

HTTP Caching原理图:
在这里插入图片描述

可缓存的响应
并不是所有响应都是可以缓存的,其中既有安全性的考虑,也有管理策略的需求,还有现实的实现情况。
安全性的考虑:对于HTTP请求中包含Authentication头字段的响应,或包含Set-Cookie头字段的响应,缺省不会保存在public cache中,因为它们包含个人信息。如果确实需要保存,需要显示的设置Cache-Control: public, s-maxage,或must-revalidate中的一个。
什么样的响应会被缓存呢?RFC7234上写的很复杂,一般理解为GET方法,状态码是200,206,301,404的,并在响应中指定了缓存的新鲜度的响应就可以被缓存。
什么样的响应不会被缓存?其它方法,除非构造了合适的cache key,并被cache理解和支持;明确设置了Cache-Control: no-store的也不会被缓存。

响应的新鲜度:
网络上的资源并不是一成不变的,并且变化频率也不一样,对于某些资源可能实时在变化,某些资源有固定的更新周期,而另外一些资源可能10年8年也不会改变。按照上面缓存的工作逻辑,只有第一次请求发送到了origin server,后继的请求/响应都直接由cache返回,显然不能满足需求。为了解决这个问题,HTTP cache中引入了新鲜度的概念,为不同更新频度的资源指定不同的过期时间,新鲜度从响应生成时刻算起Date,到指定的过期时间Expiration time。即fressless _lifetime = Expiration time - Date,【注:如果是s-maxage或max-age这样的相对时间,则它们的值就是fressless _lieftime】相当于是给响应贴了一个保质期的标签,资源的状态在保质期内的称为fresh,否则称为stale。Expiration time可以有几个来源,分别是s-maxage,max-age,Expires,优先级也是从高到低,其中s-maxage仅针对share cache。另外,响应可能保存在某个中间的proxy cache上,当用户发请求获取时,响应已经保存在proxy cache一段时间,HTTP Cache引入了一个头字段Age, 标记自响应产生以来,到proxy cache发送回应时,响应经历的时间。所以判断一个响应是否新鲜的公式是fressness_lifetime>current_age。current_age的计算有比较复杂的逻辑,可以参考RFC7234,此处略过。
注:还有一个所谓启发式的过期时间,即如果上述决定新鲜度的时间origin server都没有提供,cache可以自行依据Last-Modifed字段和Date,计算一个新鲜度时间,通常是(Date - Last_modified)/10。

控制缓存的头字段:
Expires 响应保质期的绝对时间
Cache-Control 目前控制缓存策略的主要头字段
其它,如Pragma, 等效于发送方向的Cache-Control: no-cache,浏览器中强制刷新时可以看到,纯粹是为了兼容HTTP/1.0,可理解为已经废弃。

Cache-Control头字段:
有3点需要说明:

  1. 对于缓存的控制是单向的,对一个方向施加的指令,并不代表对另外一个方向施加了相同的指令,尽管某些指令在发送和接收方同时存在,但它们表达的语义和作用是不同的。
  2. 指令的施加是对请求/应答链上所有的HTTP Cache施加的,并不能指定对某个特定的HTTP cache施加某个指令。
  3. 某些指令仅针对share cache生效,对于private cache则忽略。如s-maxage, proxy-revalidate。

接收方向的Cache-Control directive
max-age: 指定资源【即术语representations】的过期时间。
s-maxage: 指定资源的过期时间,仅针对share cache。
no-store: 指示cache不缓存
no-cache: 缓存,但使用前无论缓存是否fresh都需要去origin server验证。所以可以保证客服始终获取新鲜的资源。
must-revalidate: 可以保存响应,过期则需去origin server验证,所以must-revalidate通常和max-age一起使用,如Cache-Control: must-revalidate, max-age=3600。
private/public: 决定缓存保存的位置。private仅private cache可以保存,反之,public可以保存在private和public cache。
其它还有一些头字段相对没那么重要,就略过了。

发送方向的Cache-Control directive:
max-age: 不接受age>指定时间的缓存。【注1】
max-stale: 可以接收过期的资源stale,但不能超过指定的时间。【注2】
min-fresh: 仅接收新鲜度不小于指定时间的资源
no-restore: 通知cache不要保存请求和缓存响应,并应该删除已存在的缓存。
no-cache: 缓存使用前,需要去origin server验证。
注1,注2 在MDN的表述中,并不清晰,为此博主还给他提了1个PR,并在最近一次的更新修正了,你敢信。

验证和验证器
首先要澄清的是,验证应由origin server进行,而不能由cache进行。因为验证涉及2类验证器:Last-Modified和ETage,二者都是由origin server提供。
验证的逻辑是,如果cache上的资源已经过期,当收到客户端请求,则由cache发请求询问origin server,询问是否资源已经改变,是则重发新响应返回200,否则继续使用缓存,返回304,并更新age。
如何判断资源是否改变?依据是资源的某种标识,就是上文说到的两种验证器:Last-Modified和ETag。
验证流程是:

  1. origin server回应响应时携带了验证器。
  2. 需要验证时,如资源已经过期,或指定缓存指令是Cache-Control: no-cache,强制要求验证,则请求中通过条件请求字段:If-Modified-Since或If-None-Match携带1中的验证器。
  3. 验证通过则返回304,不返回包体,并更新age,节省了带宽,否则返回完整的包体和200。

HTTP cache验证原理图:
在这里插入图片描述

Vary和cache
cache是一个本地存储,同时提供了机制检索缓存,检索使用的是URI+请求方式,或省略为URI,称为cache key,或primary cache key。在有内容协商的情况下,单用URL并不能区分缓存。此时通过Vary字段标记,哪些字段会影响到缓存,把Vary字段标记的请求字段作为缓存的secordary key[注3]
注3:MDN举例的图中这块错了,其中Content-Encoding应该是Accept-Encoding,博主指出,并被确认,但现在还没改过来。

Varying response原理图
在这里插入图片描述

网页开发的Revved策略
前面说过,对于某些资源,如CSS,JS可能10年8年不会变,我们可以为它指定一个超长的过期时间。但万一这个拥有超长缓存时间的资源发生了变化怎么办?此时可以从资源命名着手,采取所谓cache busting的技术,给文件名添加版本号h

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
引用: Caching allocator 是一种对象缓存内核内存分配器。它的作用是在内核中分配和管理内存,以提高内存分配的效率和性能。引用: slab cache 是一种针对每个CPU分配一个本地缓存的数据结构,也可以称为本地缓存。它的目的是减少对全局内存分配器的竞争,从而提高内存分配和释放的速度。引用: 在文章目录中可以看到关于智能 Allocator 和 bitmap_allocator 的内容,这些都是与内存分配相关的不同实现方式。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [The Slab Allocator An Object-Caching Kernel Memory Allocator](https://download.csdn.net/download/redkowl/10275414)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 33.333333333333336%"] - *2* [Linux内存管理(二十五):slab 分配器概述](https://blog.csdn.net/jingerppp/article/details/127749743)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 33.333333333333336%"] - *3* [GNU C++ Allocator分类总结与归纳](https://blog.csdn.net/yueguangmuyu/article/details/115831216)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 33.333333333333336%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值