浏览器的缓存
- 开始加载,域名解析,
DNS 缓存
本地缓存(memory 缓存)
HTTP 缓存(强缓存和协商缓存)
服务端缓存(CDN 缓存)
1. DNS 缓存
www.abc.com (域名) - DNS 解析 -> 8.8.8.8 (IP 地址)
- 首先搜索浏览器自身的 DNS 缓存,如果存在,则域名解析到此完成。
- 如果浏览器自身的缓存里面没有找到对应的条目,那么会尝试读取操作系统的 hosts 文件看是否存在对应的映射关系,如果存在,则域名解析到此完成。
- 如果本地 hosts 文件不存在映射关系,则查找本地 DNS 服务器(ISP 服务器,或者自己手动设置的 DNS 服务器),如果存在,域名到此解析完成。
- 如果本地 DNS 服务器还没找到的话,它就会向根服务器发出请求,进行迭代查询。
以上过程会对网络请求带来一定的损耗,所以浏览器在第一次获取到 IP 地址后,会将其缓存起来。 下次相同域名再次发起请求时,浏览器会先查找本地缓存,如果缓存有效,则会直接返回该 IP 地址,否则会继续开始寻址之旅。
2. memory cache(本地缓存)
memory cache 是浏览器为了加快读取缓存速度而进行的自身的优化行为,不受开发者控制,也不受 HTTP 协议头的约束。 当资源被存入内存后,下次同样的请求将不再通过网络,而是直接访问内存, 当关闭该页面时,此资源就被内存释放掉了,再次重新打开相同页面时不再出现 from memory cache
的情况。
几乎所有的网络请求资源都会根据相关的策略被浏览器自动加入到 memory cache 中。 但是也正因为数量很大但是浏览器占用的内存不能无限扩大这样两个因素,MemoryCache 注定只能是个 短期存储
。 当数据量过大,即使网页不关闭,缓存依然会失效。
memory cache 机制保证了一个页面中如果有两个相同的请求实际只会被请求最多一次,避免浪费。
3. disk cache(硬盘缓存)
硬盘缓存取决于 HTTP 中的响应头信息,它也是浏览器缓存中最重要的内容。 因为你想啊,DNS 缓存它主要是做一个 ip 地址查找并且是自主完成的,memory cache 也是不受控制,算是一个黑盒。 所以剩下的可以受我们控制的硬盘缓存的重要性就不言而喻了,大多优化手段也是针对硬盘缓存。
HTTP 所控制下的 disk cache 缓存分为 强制缓存
和 协商缓存
强制缓存 (也叫强缓存)
对于强缓存,控制它的字段分别是:Expires
和 Cache-Control
,其中 Cache-Control 优先级比 Expires 高。 Cache-Control 和 Expires 分别是 HTTP/1.1 和 HTTP/1.0 的内容,为了兼容 HTTP/1.0 和 HTTP/1.1,实际项目中两个字段都会设置。
Date: Thu, 30 Apr 2020 12:39:56 GMT // 格林威治时间
Cache-Control: max-age=3600 // 这个资源缓存起来,缓存时间是3600秒(1小时)
Expires: Thu, 10 Nov 2020 08:45:11 GMT // 到达指定时间过期
Date: Thu, 30 Apr 2020 12:39:56 GMT // 格林威治时间
Cache-Control: max-age=3600 // 这个资源缓存起来,缓存时间是3600秒(1小时)
Expires: Thu, 10 Nov 2020 08:45:11 GMT // 到达指定时间过期
浏览器收到这个响应之后就会做下面的事情
- 浏览器把这次请求得到的响应体缓存到本地文件中
- 浏览器标记这次请求的请求方法和请求路径
- 浏览器标记这次缓存的时间是 3600 秒
- 浏览器记录服务器的响应时间是格林威治时间 2020-04-30 12:39:56
Cache-Control 还可以设置下面一个或多个值:
public
:指示服务器资源是公开的。比如有一个页面资源,所有人看到的都是一样的。这个值对于浏览器而言没有什么意义,但可能在某些场景可能有用。本着「我告知,你随意」的原则,http 协议中很多时候都是客户端或服务器告诉另一端详细的信息,至于另一端用不用,完全看它自己。private
:指示服务器资源是私有的。比如有一个页面资源,每个用户看到的都不一样。这个值对于浏览器而言没有什么意义,但可能在某些场景可能有用。本着「我告知,你随意」的原则,http 协议中很多时候都是客户端或服务器告诉另一端详细的信息,至于另一端用不用,完全看它自己。no-cache
:告知客户端,你可以缓存这个资源,但是不要直接使用它。当你缓存之后,后续的每一次请求都需要附带缓存指令,让服务器告诉你这个资源有没有过期。no-store
:告知客户端,不要对这个资源做任何的缓存,之后的每一次请求都按照正常的普通请求进行。若设置了这个值,浏览器将不会对该资源做出任何的缓存处理。max-age
:不再赘述
协商缓存
一旦发现缓存无效,它并不会简单的把缓存删除,而是抱着一丝希望,想问问服务器,我这个缓存还能继续使用吗? 于是,浏览器向服务器发出了一个带缓存请求头的请求:
// 这个资源的上一次修改时间是格林威治时间 2020-04-30 08:16:31,这个时间之后有发生变动吗?
If-Modified-Since: Thu, 30 Apr 2020 08:16:31 GMT
// 这个资源的编号是W/"121-171ca289ebf,这个资源的编号发生变动了吗?
If-None-Match: W/"121-171ca289ebf"
// 之所以要发两个信息,是为了兼容不同的服务器,
// 因为有些服务器只认If-Modified-Since,有些服务器只认If-None-Match,
// 有些服务器两个都认,但是一般来说 If-None-Match 的优先级高于 If-Modified-Since
// 这个资源的上一次修改时间是格林威治时间 2020-04-30 08:16:31,这个时间之后有发生变动吗?
If-Modified-Since: Thu, 30 Apr 2020 08:16:31 GMT
// 这个资源的编号是W/"121-171ca289ebf,这个资源的编号发生变动了吗?
If-None-Match: W/"121-171ca289ebf"
// 之所以要发两个信息,是为了兼容不同的服务器,
// 因为有些服务器只认If-Modified-Since,有些服务器只认If-None-Match,
// 有些服务器两个都认,但是一般来说 If-None-Match 的优先级高于 If-Modified-Since
此时可能会产生两个结果
缓存失效
:服务器再次给予一个正常的响应(响应码 200 带响应体),同时可以附带上新的缓存指令,浏览器缓存新的内容缓存有效
:服务器返回 304 重定向,并且响应头带上新的缓存指令(不再返回数据避免浪费),浏览器作出相应缓存动作。
Date: Thu, 30 Apr 2020 12:39:56 GMT // 格林威治时间
Etag: W/"121-171ca289ebf", // 这个资源的编号是 W/"121-171ca289ebf"
Last-Modified: Thu, 30 Apr 2020 08:16:31 GMT, // 这个资源的上一次修改时间
Date: Thu, 30 Apr 2020 12:39:56 GMT // 格林威治时间
Etag: W/"121-171ca289ebf", // 这个资源的编号是 W/"121-171ca289ebf"
Last-Modified: Thu, 30 Apr 2020 08:16:31 GMT, // 这个资源的上一次修改时间
4. CDN 缓存
CDN (全称 Content Delivery Network),即内容分发网络, 通过将静态资源(js、css、图片等)缓存到离用户很近的相同网络运营商的 CDN 节点上,即 CDN 缓存。
原理
应用 CDN 后,DNS 返回的不再是 IP 地址,而是一个 CNAME(Canonical Name ) 别名记录,指向 CDN 的全局负载均衡, 由于没有返回 IP 地址,于是本地 DNS 会向负载均衡系统再发送请求 ,则进入到 CDN 的全局负载均衡系统进行智能调度:
- 看用户的 IP 地址,查表得知地理位置,找相对最近的边缘节点
- 看用户所在的运营商网络,找相同网络的边缘节点
- 检查边缘节点的负载情况,找负载较轻的节点
- 其他,比如节点的“健康状况”、服务能力、带宽、响应时间等
缓存代理
缓存系统是 CDN 的另一个关键组成部分,缓存系统会有选择地缓存那些最常用的那些资源 其中有两个衡量 CDN 服务质量的指标:
- 命中率:用户访问的资源恰好在缓存系统里,可以直接返回给用户,命中次数与所有访问次数之比
- 回源率:缓存里没有,必须用代理的方式回源站取,回源次数与所有访问次数之比
缓存系统也可以划分出层次,分成一级缓存节点和二级缓存节点。 一级缓存配置高一些,直连源站,二级缓存配置低一些,直连用户。
回源的时候二级缓存只找一级缓存,一级缓存没有才回源站,可以有效地减少真正的回源
现在的商业 CDN 命中率都在 90% 以上,相当于把源站的服务能力放大了 10 倍以上