4.4. Invalidation
动机
Because unsafe request methods (Section 4.2.1 of [RFC7231]) such as
PUT, POST or DELETE have the potential for changing state on the
origin server, intervening caches can use them to keep their contents
up to date.
unsafe request methods可能会改变服务器的状态,相关的caches可以利用这些请求来实现"keep their contents up to date",实际指的就是执行"Invalidation"操作。
"Invalidate"操作
- 删掉和effective request URI相关的stored response
- 或者将这些stored response标记为"invalid",在验证以后提供给后续请求。(可以提供是因为符合successfully validated吗?)
non-error response code
2xx 或者 3xx
"Invalidate"范围
必须(MUST)Invalidate
1.Unsafe request method
当收到一个包含unsafe request method 的response时,如果包含non-error status code,则cache必须(MUST)Invalidate符合如下条件的stored response:
- 和effective request URI相关的stored response
- 和Location指向effective URI相关的stored response
- 和Content-Location指向effective URI相关的stored response
客户端请求:
POST /api/drafts/99?action=publish HTTP/1.1
Host: www.notion-clone.com
Authorization: Bearer top-secret-token
{"status": "public"}
effective request URI:
服务器响应:
HTTP/1.1 201 Created
Status: 201 Created
Location: /articles/2026/001
Content-Location: /articles/latest
{"id": "2026-001", "title": "Understanding RFC 7234"}
Location 对应的 Effective URI:
Content-Location 对应的 Effective URI:
cache会Invalidation如下effective URI对应的stored response:
- www.notion-clone.com/api/drafts/…
- www.notion-clone.com/articles/20…
- www.notion-clone.com/articles/la…
2.unknow method
当cache收到一个non-error response,但是它的method的safety未知时,一定要(MUST)invalidate对应的effective request URI
不能(MUST NOT)Invalidate
在满足前面MUST的情况时,如果Location or Content-Location对应的URI的host和effective request URI中的host不同时,不要(MUST NOT)进行 Invalidate操作。这可以帮助避免denial-of-service attacks。
1.客户端请求 (Request)
你访问了攻击者的网站 evil-attacker.com 并提交了一个表单(不安全方法)。
POST /submit-form HTTP/1.1
Host: evil-attacker.com
Content-Type: application/x-www-form-urlencoded
data=malicious_payload
该请求的 Effective Request URI:
2.服务器响应 (Response) - 恶意触发
攻击者的服务器返回成功,但试图利用 Location 头部去“碰瓷” Google 的资源。
HTTP/1.1 200 OK
Location: https://www.google.com/search?q=weather
响应中 Location 对应的 Effective URI:
https://www.google.com/search?q=weather
3.如果没有这条规则
显然根据前面MUST的规则,cache中对应的缓存会被删掉。下次访问谷歌搜索时必须重新下载资源,增加了服务器负担(Dos),同时上网速度变慢了。
4.执行规则 当cache收到这个响应时:
1.提取请求 Host: evil-attacker.com
2.提取 Location Host: www.google.com
3.比对结果: evil-attacker.com 不等于 www.google.com
4.最终动作: 不执行失效 (No Invalidation)
定义冲突:当POST可缓存时
Responses to POST requests are only cacheable when they include
explicit freshness information (see Section 4.2.1 of [RFC7234]).
However, POST caching is not widely implemented. For cases where an
origin server wishes the client to be able to cache the result of a
POST in a way that can be reused by a later GET, the origin server
MAY send a 200 (OK) response containing the result and a
Content-Location header field that has the same value as the POST's
effective request URI (Section 3.1.4.2).
这就和前面"必须(MUST)Invalidate"的定义冲突了
局限
这里定义的Invalidate操作不能保证能操作到所有对应的stored response。例如可能只能操作到request经过的cache.
场景案例:新闻图片的全球更新
假设你正在维护一个高流量的新闻网站,其中一张关键图片 breaking_news.jpg 存储在源服务器上,并通过 CDN 在各地进行分发。
1. 初始状态(各节点已缓存
- 源服务器 (Origin): 存储着 breaking_news.jpg (旧版本 V1)。
- CDN 节点 A (北京): 已缓存 V1。
- CDN 节点 B (上海): 已缓存 V1。
2. 触发动作(更新资源) 管理员在后台上传了新的图片 (V2),并发送了一个 PUT /static/breaking_news.jpg 请求来同步源服务器。
3.Invalidate过程(路径依赖)
由于网络路由的随机性,这个 PUT 请求的传输路径如下:
管理员终端 -> CDN 节点 A (北京) -> 源服务器
- 节点 A 的反应: 作为一个标准的 HTTP 缓存,节点 A 观测到了针对该路径的写操作。根据 RFC 规范,它立即将自己本地缓存的 breaking_news.jpg (V1) 标记为失效 (Invalidated)或者删除。
4. 产生的问题(同步盲点)
- 节点 B 的状态: 由于 PUT 请求并未经过 节点 B (上海),节点 B 对这次更新完全不知情。它的本地存储中依然保留着旧版本 V1,且认为该缓存尚未过期。
解决办法
方案1:
对于同一文件,每当内容发生变化以后,在服务器端名字就变化一次。客户端从服务器获取到新名字以后,就可以及时清理本地缓存。
例如当服务器上某个用户头像发生改变时,就改变头像图片的名字,客户端用最新的名字请求时,就可以及时的清理本地缓存(okhttp级别或者它的依赖库级别)。
方案2:
对于固定路径的本地文件,或者固定URL的的系统,手动获取文件signature,当两次请求中,signature发生变化时,才清理本地缓存。
例如:
public static void loadLocalFile(Context context, File file, ImageView imageView) {
Glide.with(context)
.load(file)
// 只有当文件真的存在时,才获取时间戳作为签名
.signature(new ObjectKey(file.lastModified()))
.into(imageView);
}