① 浏览器缓存机制
有dns的地方,就有缓存。浏览器、操作系统、Local DNS、根域名服务器,它们都会对DNS结果做一定程度的缓存。
DNS查询过程如下:
首先搜索浏览器自身的DNS缓存,如果存在,则域名解析到此完成。
如果浏览器自身的缓存里面没有找到对应的条目,那么会尝试读取操作系统的hosts文件看是否存在对应的映射关系,如果存在,则域名解析到此完成。
如果本地hosts文件不存在映射关系,则查找本地DNS服务器(ISP服务器,或者自己手动设置的DNS服务器),如果存在,域名到此解析完成。
如果本地DNS服务器还没找到的话,它就会向根服务器发出请求,进行递归查询。
浏览器本地缓存失效后,浏览器会向CDN边缘节点发起请求。类似浏览器缓存,CDN边缘节点也存在着一套缓存机制。CDN边缘节点缓存策略因服务商不同而不同,但一般都会遵循http标准协议,通过http响应头中的
Cache-control: max-age 的字段来设置CDN边缘节点数据缓存时间。
当浏览器向CDN节点请求数据时,CDN节点会判断缓存数据是否过期,若缓存数据并没有过期,则直接将缓存数据返回给客户端;否则,CDN节点就会向服务器发出回源请求,从服务器拉取最新数据,更新本地缓存,并将最新数据返回给客户端。 CDN服务商一般会提供基于文件后缀、目录多个维度来指定CDN缓存时间,为用户提供更精细化的缓存管理。
CDN 优势
CDN节点解决了跨运营商和跨地域访问的问题,访问延时大大降低。
大部分请求在CDN边缘节点完成,CDN起到了分流作用,减轻了源服务器的负载。
http请求报文(request)
请求行
请求方法 空格 URL 空格 协议版本 回车符 换行符
请求头(通用信息头、请求头、实体头)
头部字段名 冒号 值 回车键 换行符
...
头部字段名 冒号 值 回车键 换行符
空行
回车符 换行符
实体主体(只有post请求有)
主体
http响应报文(response)
状态行
协议版本 空格 状态码 空格 状态码描述 回车符 换行符
响应头部
头部字段名 冒号 值 回车符 换行符
...
头部字段名 冒号 值 回车符 换行符
空行
回车符 换行符
响应正文
正文
浏览器初次向服务器发起请求后拿到请求结果,会根据响应报文中HTTP头的缓存标识,决定是否缓存返回的结果,是则将请求结果和缓存标识存入浏览器缓存中
浏览器每次发起请求,都会现在浏览器缓存中查找该请求的结果以及缓存标识
浏览器 浏览器缓存 服务器
——————第一次发起http请求——————>
<——没有该请求的缓存结果和缓存标识————
——————————————发起http请求——————————————>
<——————————返回该请求结果和缓存规则————————————
——将请求结果和缓存标识存入浏览器缓存——>
强制缓存就是向浏览器缓存查找结果,并根据该结果的缓存规则来决定是否使用该缓存结果的过程
强制缓存的情况分为三种:
1、不存在该缓存结果和缓存标识,强制缓存失效,直接向服务器发起请求
2、存在该缓存结果和缓存标识,但结果已经失效,强制缓存失效,使用协商缓存
3、存在该缓存结果和缓存标识,且该结果没有失效,强制缓存生效,直接返回该结果
控制强制缓存的字段:Expires,Cache-Control
Expires 是 HTTP/1.0 控制缓存的字段,值为服务器返回该请求的结果缓存时间
即再次发送请求是,客户端时间 小于 Expires的值,直接使用缓存结果
Cache-Control 是HTTP/1.1的规则,主要用于控制网页缓存,主要取值为:
public:所有的内容都缓存(客户端和代理服务器都可以缓存)
private:所有内容只有客户端可以缓存(默认值)
no-cache:客户端缓存内容,但是是否使用缓存则需要经过协商缓存来验证决定
no-store:即不使用强制缓存,也不使用协商缓存
max-age=xxx:缓存内容将在xxx秒后失效
Expires 是一个绝对值
Cache-Control 中 max-age 是相对值,解决了 Expires时期 服务端与客户端 可能出现时间差的问题
注:Expires和Cache-Control同时存在时,只有Cache-Control生效
协商缓存就是强制缓存失效后,浏览器携带缓存标识向服务器发起请求,由服务器根据缓存标识决定是否使用缓存的过程
协商缓存的两种情况:
1、协商缓存生效,返回304,继续使用缓存
过程:
浏览器 浏览器缓存 服务器
————————发起http请求————————>
<——该请求的缓存结果失效,只返回缓存标识——
————————携带该资源的缓存标识,发起http请求————————>
<—————————————304,该资源无更新————————————
——————获取该请求的缓存结果——————>
<——————返回该请求的缓存结果——————
2、协商缓存失败,返回200和请求结果
过程:
浏览器 浏览器缓存 服务器
————————发起http请求————————>
<——该请求的缓存结果失效,只返回缓存标识——
————————携带该资源的缓存标识,发起http请求————————>
<————————200,资源已更新,重新返回请求和结果———————
——将该请求结果和缓存标识存入浏览器缓存中—>
协商缓存的标识也是在响应报文的HTTP头中和请求结果一起返回给浏览器的
控制协商缓存的字段:
(1) Last-Modified/If-Modified-Since:Last-Modified是服务器响应请求是,返回该资源文件在服务器最后被修改的时间;If-Modified-Since再次发起请求时,携带上次返回的Last-Modified的值,服务器将该字段值与该资源最后修改时间对比,决定是否用缓存
(2)Etag/If-None-Match:Etag服务器响应请求时,返回当前资源文件的一个唯一标识,由服务器生成之;If-None-Match是再次发起请求时,携带上次返回的唯一标识Etag的值,服务器收到后,将该字段值与该资源在服务器上的Etag对比,一致 则返回304,否则返回200
注:Etag/If-None-Match优先级高于Last-Modified/If-Modified-Since,同时存在时只有Etag/If-None-Match生效
浏览器缓存分为:内存缓存 和 硬盘缓存
内存缓存特性:
(1)快速读取:内存缓存会将编译解析后的文件,存入该进程的内存中,便于下次运行时快速读取
(2)时效性:一旦关闭进程,进程内存清空
硬盘缓存特性:
永久性:直接写入硬盘文件中
复杂、缓慢:读取缓存对该缓存存放的硬盘文件进行I/O操作,重新解析
from memory cache:使用内存中的缓存
from disk cache:使用硬盘中的缓存
浏览器读取顺序:memory ——> disk
浏览器将js和图片等文件解析执行后直接存入内存缓存中,F5刷新页面时,from memory cache(使用内存中的缓存)
css文件存入硬盘中,F5刷新页面时,from disk cache(使用硬盘中的缓存)
参考文章
https://segmentfault.com/a/1190000017962411
https://www.cnblogs.com/chengxs/p/10396066.html
② 协商缓存和强缓存的区别
协商缓存和强缓存的区别
(1)强缓存
使用强缓存策略时,如果缓存资源有效,则直接使用缓存资源,不必再向服务器发起请求。
强缓存策略可以通过两种方式来设置,分别是 http 头信息中的 Expires 属性和 Cache-Control 属性。
(1)服务器通过在响应头中添加 Expires 属性,来指定资源的过期时间。在过期时间以内,该资源可以被缓存使用,不必再向服务器发送请求。这个时间是一个绝对时间,它是服务器的时间,因此可能存在这样的问题,就是客户端的时间和服务器端的时间不一致,或者用户可以对客户端时间进行修改的情况,这样就可能会影响缓存命中的结果。
(2)Expires 是 http1.0 中的方式,因为它的一些缺点,在 HTTP 1.1 中提出了一个新的头部属性就是 Cache-Control 属性,它提供了对资源的缓存的更精确的控制。它有很多不同的值,
Cache-Control可设置的字段:
public:设置了该字段值的资源表示可以被任何对象(包括:发送请求的客户端、代理服务器等等)缓存。这个字段值不常用,一般还是使用max-age=来精确控制;
private:设置了该字段值的资源只能被用户浏览器缓存,不允许任何代理服务器缓存。在实际开发当中,对于一些含有用户信息的HTML,通常都要设置这个字段值,避免代理服务器(CDN)缓存;
no-cache:设置了该字段需要先和服务端确认返回的资源是否发生了变化,如果资源未发生变化,则直接使用缓存好的资源;
no-store:设置了该字段表示禁止任何缓存,每次都会向服务端发起新的请求,拉取最新的资源;
max-age=:设置缓存的最大有效期,单位为秒;
s-maxage=:优先级高于max-age=,仅适用于共享缓存(CDN),优先级高于max-age或者Expires头;
max-stale[=]:设置了该字段表明客户端愿意接收已经过期的资源,但是不能超过给定的时间限制。
一般来说只需要设置其中一种方式就可以实现强缓存策略,当两种方式一起使用时,Cache-Control 的优先级要高于 Expires。
no-cache和no-store很容易混淆:
no-cache 是指先要和服务器确认是否有资源更新,在进行判断。也就是说没有强缓存,但是会有协商缓存;
no-store 是指不使用任何缓存,每次请求都直接从服务器获取资源。
(2)协商缓存
如果命中强制缓存,我们无需发起新的请求,直接使用缓存内容,如果没有命中强制缓存,如果设置了协商缓存,这个时候协商缓存就会发挥作用了。
上面已经说到了,命中协商缓存的条件有两个:
max-age=xxx 过期了
值为no-store
使用协商缓存策略时,会先向服务器发送一个请求,如果资源没有发生修改,则返回一个 304 状态,让浏览器使用本地的缓存副本。如果资源发生了修改,则返回修改后的资源。
协商缓存也可以通过两种方式来设置,分别是 http 头信息中的Etag 和Last-Modified属性。
(1)服务器通过在响应头中添加 Last-Modified 属性来指出资源最后一次修改的时间,当浏览器下一次发起请求时,会在请求头中添加一个 If-Modified-Since 的属性,属性值为上一次资源返回时的 Last-Modified 的值。当请求发送到服务器后服务器会通过这个属性来和资源的最后一次的修改时间来进行比较,以此来判断资源是否做了修改。如果资源没有修改,那么返回 304 状态,让客户端使用本地的缓存。如果资源已经被修改了,则返回修改后的资源。使用这种方法有一个缺点,就是 Last-Modified 标注的最后修改时间只能精确到秒级,如果某些文件在1秒钟以内,被修改多次的话,那么文件已将改变了但是 Last-Modified 却没有改变,这样会造成缓存命中的不准确。
(2)因为 Last-Modified 的这种可能发生的不准确性,http 中提供了另外一种方式,那就是 Etag 属性。服务器在返回资源的时候,在头信息中添加了 Etag 属性,这个属性是资源生成的唯一标识符,当资源发生改变的时候,这个值也会发生改变。在下一次资源请求时,浏览器会在请求头中添加一个 If-None-Match 属性,这个属性的值就是上次返回的资源的 Etag 的值。服务接收到请求后会根据这个值来和资源当前的 Etag 的值来进行比较,以此来判断资源是否发生改变,是否需要返回资源。通过这种方式,比 Last-Modified 的方式更加精确。
当 Last-Modified 和 Etag 属性同时出现的时候,Etag 的优先级更高。使用协商缓存的时候,服务器需要考虑负载平衡的问题,因此多个服务器上资源的 Last-Modified 应该保持一致,因为每个服务器上 Etag 的值都不一样,因此在考虑负载平衡时,最好不要设置 Etag 属性。
总结:
强缓存策略和协商缓存策略在缓存命中时都会直接使用本地的缓存副本,区别只在于协商缓存会向服务器发送一次请求。它们缓存不命中时,都会向服务器发送请求来获取资源。在实际的缓存机制中,强缓存策略和协商缓存策略是一起合作使用的。浏览器首先会根据请求的信息判断,强缓存是否命中,如果命中则直接使用资源。如果不命中则根据头信息向服务器发起请求,使用协商缓存,如果协商缓存命中的话,则服务器不返回资源,浏览器直接使用本地资源的副本,如果协商缓存不命中,则浏览器返回最新的资源给浏览器。
③ 前端浏览器缓存机制
在前端开发中,性能是一个永恒的话题,没有最好,只有更好。判断一个网站性能好坏,一个直入眼观的即是网页的反应速度,有一个方式就是使用缓存,一个优秀的缓存策略可以缩短网页请求的时间,减少延迟,并且网页可以重复利用,还可以减少带宽,降低网络负荷。
1: 为什么需要缓存?
a:缓存可以减少用户等待时间,提升用户体验
b:减少网络带宽消耗
c:降低服务器压力
Note:缓存使用不当,也会造成‘脏数据’问题
2:常见的缓存类型
强缓存 -
Expires服务器端设置,表示该资源的过期时间,会有弊端,客户端时间和服务器时间不一致的问题。
Cache-Control:max-age表示缓存资源的最大生命周期,单位是秒
所以Expires 结合 Cache-Control 一起使用,大型网站中一般比较适用
协商缓存-
Last-Modified:值为资源的最后更新时间,随服务器response返回
If-Modified-Since:通过比较两个时间来判断资源在两次请求期间是否有过修改,如果没有,则命中协商缓存
Etag:表示资源内容的唯一标识,即资源的消息摘要
If-None-Match:服务器通过比较请求头中的If-None-Match与当前资源的Etag是否一致来判断资源是否在两次请求期间有过修改
3:缓存流程图示:
a:浏览器会先检测强缓存类型(Cache-Control 或者 Expires)是否有效;命中直接浏览器本地获取缓存资源
b:未命中。服务器会根据请求头Request Header验证这个资源是否命中协商缓存,称之为HTTP二次验证,命中,服务器返回请求,但返回资源,而是告诉客户端直接中直接从浏览器缓存中获取
Note:
1.强缓存不会发生请求,协商缓存存在服务器请求
2.当协商缓存也未命中时,则服务器会将资源发送到客户端
3.F5刷新页面,会跳过强缓存
4.Ctrl+F5刷新页面,跳过强缓存和协商缓存
5.不会缓存的情况
HTTPS POST请求 根据Cookie获取认证信息 Request Header Cache-Control:no-cache, max-age=0
6.小故事大道理
上文对整个概念做了阐述,还是不够形象,我们来通过几个小故事生动理解一下:
故事一:Last-Modified
浏览器:Hi,我需要 jartto.min.js 这个文件,如果是在 Last-Modified: Fri Feb 15 2019 19:57:31 GMT 之后修改过的,请发给我。
服务器:(检查文件的修改时间)
服务器:Oh,这个文件在那个时间之后没有被修改过,你已经有最新的版本了。
浏览器:太好了,那我就显示给用户了。
故事二:ETag
浏览器:Hi,我需要 jartto.css 这个文件,有没有不匹配 3c61f-1c1-2aecb436 这个串的
服务器:(检查 ETag…)
服务器:Hey,我这里的版本也是 3c61f-1c1-2aecb436,你已经是最新的版本了
浏览器:好,那就可以使用本地缓存了
④ 前端缓存的理解 或者 前端数据持久化的理解(强制缓存、协商缓存)
缓存可以说是性能优化中简单高效的一种优化方式了。一个优秀的缓存策略可以缩短网页请求资源的距离,减少延迟,并且由于 缓存文件可以重复利用 ,还可以减少带宽,降低网络负荷。
对于一个数据请求来说,可以分为发起 网络请求、后端处理、浏览器响应 三个步骤。浏览器缓存可以帮助我们在第一和第三步骤中优化性能。比如说直接使用缓存而不发起请求,或者发起了请求但后端存储的数据和前端一致,那么就没有必要再将数据回传回来,这样就减少了响应数据。
①不存在该缓存结果和缓存标识,强制缓存失效,则直接向服务器发起请求
②存在该缓存结果和缓存标识,但该结果已失效,强制缓存失效,则使用协商缓存
③存在该缓存结果和缓存标识,且该结果尚未失效,强制缓存生效,直接返回该结果
控制强制缓存的字段分别是Expires和Cache-Control,其中Cache-Control优先级比Expires高。
Cache-Control、Expires都是缓存到期时间,Cache-Control是相对值,Expires是绝对值,即再次发送请求时,如果时间没到期,强制缓存生效。
注:在无法确定客户端的时间是否与服务端的时间同步的情况下,Cache-Control相比于expires是更好的选择,所以同时存在时,只有Cache-Control生效。
①协商缓存生效,返回304
②协商缓存失效,返回200和请求结果
这里我们以博客的请求为例,状态码为灰色的请求则代表使用了强制缓存,请求对应的Size值则代表该缓存存放的位置,分别为from memory cache 和 from disk cache。那么from memory cache 和 from disk cache又分别代表的是什么呢?什么时候会使用from disk cache,什么时候会使用from memory cache呢?
from memory cache代表使用 内存中的缓存 ,from disk cache则代表使用的是 硬盘中的缓存 ,
⑤ Okhttp解析(五)缓存的处理
大家好,之前我们讲解了Okhttp网络数据请求相关的内容,这一节我们讲讲数据缓存的处理。本节按以下内容讲解Okhttp缓存相关的内容。
缓存的使用场景很多,通过它可以将数据通过一定的规则存储起来,再次请求数据的时候就可以快速从缓存中读取了,缓存有以下优势。
HTTP本身提供了一套缓存相关的机制。这套机制定义了相关的字段和规则,用来客户端和服务端进行缓存相关的协商,如响应的数据是否需要缓存,缓存有效期,缓存是否有效,服务器端给出指示,而客户端则根据服务端的指示做具体的缓存更新和读取缓存工作。http缓存可以分为两类:
强制缓存,在缓存数据未失效的情况下,可以直接使用缓存数据,有两个字段Expires和Cache-Control用于标明失效规则。
表示过期时间,由服务端返回。那么下次请求数据时,判断这个Expires过期时间是否已经过了,如果还没有到过期时间,则使用缓存,如果过了过期时间,则重新请求服务器的数据。Expires格式如下:
不过因为服务器和客户端的时间并不是同步的,用一个绝对时间作为过期的标记并不是很明智,所以HTTP1.1之后更多的是Cache-Control,它的控制更加灵活。
表示缓存的控制,有服务端返回。它有以下几个取值:
默认情况下是private,也就是不能共享的。Cache-Control格式如下:
对比缓存,表示需要和服务端进行相关信息的对比,由服务器决定是使用缓存还是最新内容,如果服务器判定使用缓存,返回响应吗304,判定使用最新内容,则返回响应码200和最新数据。对比缓存的判定字段有两组:
ETag表示资源的一种标识信息,用于标识某个资源,由服务端返回,优先级更高。格式如下:
然后客户端再次请求时,加入字段If-None-Match,格式如下:
服务端收到请求的该字段时(之前的Etag值),和资源的唯一标识进行对比,如果相同,说明没有改动,则返回状态码304,如果不同,说明资源被改过了,则返回状态码200和整个内容数据。
Last-Modified表示资源的最近修改时间,由服务端返回,优先级更低。格式如下:
Last-Modified
由服务器返回,表示响应的数据最近修改的时间。
If-Modified-Since
由客户端请求,表示询问服务器这个时间是不是上次修改的时间。如果服务端该资源的修改时间小于等于If-Modified-Since指定的时间,说明资源没有改动,返回响应状态码304,可以使用缓存。如果服务端该资源的修改时间大于If-Modified-Since指定的时间,说明资源又有改动了,则返回响应状态码200和最新数据给客户端,客户端使用响应返回的最新数据。
Last-Modified字段的值(服务端返回的资源上次修改时间),常常被用于客户端下次请求时的If-Modified-Since字段中。
HTTP的缓存规则是优先考虑强制缓存,然后考虑对比缓存。
Okhttp缓存相关的类有如下:
要开启使用Okhttp的缓存其实很简单,只需要给OkHttpClient对象设置一个Cache对象即可,创建一个Cache时指定缓存保存的目录和缓存最大的大小即可。
那么下面我们来看看Okhttp缓存执行的大概流程
Okhttp的缓存流程分为读取缓存和存储缓存两个过程,我们分别分析。
读取使用缓存的流程从HttpEngine的sendRequest发送请求开始。
接下来我们分析
从Cache的get方法开始。它按以下步骤进行。
如果存在缓存的话,在指定的缓存目录中,会有两个文件“****.0”和“****.1”,分别存储某个请求缓存的响应头和响应体信息。(“****”是url的md5加密值)对应的ENTRY_METADATA响应头和ENTRY_BODY响应体。缓存的读取其实是由DiskLruCache来读取的,DiskLruCache是支持Lru(最近最少访问)规则的用于磁盘存储的类,对应LruCache内存存储。它在存储的内容超过指定值之后,就会根据最近最少访问的规则,把最近最少访问的数据移除,以达到总大小不超过限制的目的。
接下来我们分析CacheStrategy缓存策略是怎么判定的。
直接看CacheStrategy的get方法。缓存策略是由请求和缓存响应共同决定的。
接来下我们看看CacheControl类里有些什么。
可以发现,它就是用于描述响应的缓存控制信息。
然后我们再看看Okhttp存储缓存是怎么进行的。
存储缓存的流程从HttpEngine的readResponse发送请求开始的。
可以看到这里先通过maybeCache写入了响应头信息,再通过cacheWritingResponse写入了响应体信息。我们再进去看Cache的put方法实现。
我们继续看Cache的writeTo方法,可以看到是写入一些响应头信息。
到这里Okhttp缓存的读取和存储流程我们就清楚了。可以说,缓存的使用策略基本都是按照HTTP的缓存定义来实现的,所以对HTTP缓存相关字段的理解是很重要的。然后关于DiskLruCache是如何管理缓存文件的,这个其实也很好理解,首先的原则就是按照LRU这种最近最少使用删除的原则,当总的大小超过限定大小后,删除最近最少使用的缓存文件,它的LRU算法是使用LinkedHashMap进行维护的,这样来保证,保留的缓存文件都是更常使用的。具体实现大家可以分析DiskLruCache和LinkedHashMap的实现原理。