- Published on
重新学习计算机网络-缓存篇
缓存
HTTP 缓存的目标是通过重用先前的响应消息来满足当前请求,从而显着提高性能
缓存类型分类
A shared cache is a cache that stores responses for reuse by more than one user; shared caches are usually (but not always) deployed as a part of an intermediary. A private cache, in contrast, is dedicated to a single user; often, they are deployed as a component of a user agent.
共享缓存是一种存储响应供多个用户重复使用的缓存;共享缓存通常(但并非总是)部署为中介的一部分。相比之下,私有缓存专用于单个用户;通常,它们被部署为用户代理的组件。
这两种类型对应了 Cache-Control
中的public
和private
的值,以及xxx
和s-xxx
一类的属性比如s-maxage
和max-age
私有缓存
私有说的就是用户的终端设备,比如电脑和手机上的浏览器、小程序,这种能够发起HTTP请求的载体。或者说是发起请求的第一环以及接收响应的最后一环。
私有缓存就存放在这些载体实现的“缓存”中。
Cache-Control: private
共享缓存
共享说的就是除了用户的终端设备以外的设备,比如中间的转发服务器、CDN服务器等,也就可以被多个用户共享。
所以如果资源跟随不同的用户发生变化时就不应该使用共享缓存了。
Cache-Control: public
对于带上Authorization
的请求,其响应如果不带上Cache-Control: public / s-maxage / must-revalidate
的话即使设置了max-age
也是不会被共享缓存的
默认行为
If a request doesn't have an Authorization header, or you are already using s-maxage or must-revalidate in the response, then you don't need to use public.
根据MDN上这句话,可以推断默认是public的
缓存方式分类
这里先介绍基于时间和基于内容的缓存方式。
这里的时间和内容是按照我自己的理解总结的,不是官方说法。
基于时间
基于时间的的缓存也就是常说的强制缓存,不管内容变化了没,没到时间就是没过期
绝对时间
以绝对时间作为一个时间点,超过这个时间便被认为这个缓存已经失效(不新鲜stale)。
这个绝对时间在服务器响应时由Expires
header给出,这个header也通常在HTTP1.0中使用用于缓存控制
Expires: Tue, 28 Feb 2022 22:22:22 GMT
使用这种方式存在两个问题:
- 依赖用户本地时间进行比较
- 精度只能到秒
- 字符串解析成时间的问题、时区问题等
经过时长
HTTP1.1中引入了cache-control
来完善缓存机制,其中的max-age
,就可以解决上面的两个问题。
Cache-Control: max-age=604800
max-age
以秒为单位,上面的例子就表示在一周的时间内都可以直接使用缓存,超过一周后这个缓存就会被标记为”过期“(stale)
如果涉及到了共享缓存,那么响应会在中间服务器上被缓存一份,如果后续其他用户访问了同样的资源,那么中间服务器会返回另一个header:age
。
这个header记录了资源在中间服务器上存放了多久,当浏览器拿到资源后,需要做响应的处理。
举个例子:
Cache-Control: max-age=604800
Age: 100
中间服务器返回了下面的header后,表示这个资源的有效期是max-age
- age
也就是 6048700秒。
同时存在
同时存在时以cache-control中的max-age为准1。
If a response includes both an Expires header and a max-age directive, the max-age directive overrides the Expires header, even if the Expires header is more restrictive. This rule allows an origin server to provide, for a given response, a longer expiration time to an HTTP/1.1 (or later) cache than to an HTTP/1.0 cache. This might be useful if certain HTTP/1.0 caches improperly calculate ages or expiration times, perhaps due to desynchronized clocks.
基于内容
基于内容则是基于我的响应内容是否改变来验证当前的缓存是否过期了。这一部分也就是常说的协商缓存。
协商是因为浏览器是不知道当前的缓存内容是不是可用的,所以要协商。
开启协商缓存需要响应头中包含
Cache-Control: no-cache
基于内容的修改时间
这种方式通过判断资源的上次修改时间是否发生变化来决定是否使用缓存。 判断在服务端进行。
设计到两个请求头:
- If-Modified-Since 由请求方发送
- Last-Modified 由响应方回复
请求响应如下:
- 浏览器请求a.js
- 服务器返回200,响应头带上
Cache-Control: no-cache
Last-Modified: Mon, 21 Oct 2024 07:28:00 GMT
- 浏览器缓存此内容
- 浏览器再次请求a.js,此时带上 If-Modified-Since:Mon, 21 Oct 2024 07:28:00 GMT
- 服务端验证a.js 在 If-Modified-Since 的值对应的日期后是否发生改变
- 改变了:返回200,并带上新的a.js内容
- 没改变:返回304,无内容(请求响应时间就会很快)
问题: 可以看到If-Modified-Since
的精度还是只能到秒,如果一秒内多次改变,是无法返回200触发更新的
基于内容是否变化
这种方式通过判断资源的摘要是否发生变化来决定是否使用缓存。 判断在服务端进行。
设计到两个请求头:
- If-None-Match 由请求方发送
- ETag 由响应方回复
请求响应如下:
- 浏览器请求a.js
- 服务器返回200,响应头带上
Cache-Control: no-cache
ETag: SomeHashABC
- 浏览器缓存此内容
- 浏览器再次请求a.js,此时带上 If-None-Match:SomeHashABC
- 服务端验证a.js 的摘要和ETag中的是否一致
- 改变了:返回200,并带上新的a.js内容
- 没改变:返回304,无内容(请求响应时间就会很快)
可以看到ETag
的出现很好解决了文件内容频繁变化的问题
同时存在
同时存在时以If-None-Match
为准,因为它更严格,更精准。
对于Cache-Control
也是一样,当存在冲突时,会尽量往不缓存的策略靠拢。
启发式
启发式的定义:启发式方法指人在解决问题时所采取的一种根据经验规则进行发现的方法。
当响应没有返回Cache-Control
等缓存相关的响应头时,就会进入启发式缓存的逻辑。
可以通过Last-Modified
以及Date
等字段计算这个资源已经多久没有更新了,以此为基础计算把这个资源缓存多久。
通常值是Last-Modified
-Date
的10%2,具体要看浏览器的实现3
HTTP header
前端构建
对于前端开发来说最简单的就是cache-control: no-cache
,每次都校验总是没错的,最保险。
但是max-age
呢?既然我我们可以通过webpack的contenthash保证每次内容变化时生成的文件名都不同,那些过期的资源就算缓存了一年,也会因为某次上次后再也无法被访问到了。
风险在哪里呢?没想到过。no-cache
的协商过程带来的FCP损失相对于访问错误资源的风险来说确实不值一提,稳定性大于一切
max-age
。┑( ̄Д  ̄)┍ 这章还是比较浅显的,后续补充一些