高级http缓存
‘壹’ 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的实现原理。
‘贰’ 前端HTTP缓存
在面试的时候总是遇到缓存相关问题,回答总是停留缓存就是从浏览器中获取,不向服务器发送请求,回答的不让面试官满意,索性抽点时间整理一下前端相关的缓存,希望下次回答的时候能够多讲点。
前端缓存分为 强缓存 和 协商缓存 ,强缓存就是在缓存未失效时,不在请求服务端,协商缓存就是去跟服务器比较是否需要重新获取资源 。
强缓存分为两种, Cache-control 和 Expires , Expires 是HTTP1.0的东西,它的值是一个格林时间,比如 Expires: Wed, 21 Oct 2015 07:28:00 GMT ,由于服务器端和浏览器端的时间差异问题,浏览器比服务器时间快,会导致缓存失效。 Cache-control 是HTTP1.1时代的新东西,设置的是一个相对时间, Cache-Control:public, max-age=31536000 ,在31536000秒后才缓存才失效, Cache-control 有很多取值。
强缓存在缓存失效内,不会从原始服务器获取新的数据,假如在缓存时段内服务器有资源更新,会导致资源获取不及时。
协商缓存有两组报文
‘叁’ http缓存之基本概念
1. 重要性
综上所述,所以大家很有必要花时间来研究。
2. 困难之处
个人认为http缓存是比较枯燥的理论知识,尤其对于前端来讲,更多在于理解概念,以及内部缓存机制,而没有什么实践可以巩固,或者说理论和现实脱轨。
浏览器会在请求资源之后,根据自己的缓存策略判断是否对资源进行缓存,当再次请求相同的资源时,浏览器根据缓存策略判断是通过本地缓存获取资源,还是重新向服务器发起请求。
这个 缓存策略 到底是什么呢?
实际每个浏览器的缓存策略是有差异的,但大致受以下几个因素的影响。
搜索关键字 禁止 html 缓存 ,很容易搜到以下答案:
但是,这是 Html 4.0 中的规范,在 Html 5.0 的规范中 http-equiv 已经不存在以上属性值了。
而且代理服务器并不会读取以上meta标签,不利于代理服务器的缓存。
-- 引用自 stackoverflow
综上所述, html meta 是一个不那么可靠,并且已经过时的解决方案,所以不建议再继续使用 。
基于 HTTP 协议的缓存策略,分为 强缓存 和 协商缓存 , 由 HTTP 协议的首部 (Headers) 信息决定。具体的操作设置需要服务器配合,比如 Nginx 。所以相对来说都是后端在做此类事情,前端接触的机会比较少。
如果开启了强缓存,并且在过期时间之内,则浏览器不再发起请求,直接使用本地的缓存资源。
Expires 和 Cache-control 用于控制强制缓存。
Expires 是 HTTP 1.0 的特性。通过指定一个明确的时间点作为缓存资源的过期时间,客户端会根据此时间点来判断到底使用本地缓存,还是向服务器重新请求资源。
优点: 在缓存过期时间内,减少客户端的 HTTP 请求,不仅节省了客户端处理时间,提高了 web 应用的执行速度,而且减少了服务器负载,以及客户端网络资源的消耗。
缺点:指定的过期时间以服务器为准,但是客户端进行过期时间判断时是将 本地的时间 与 指定的过期时间点 进行对比。如果客户端修改了本地时间,将会影响对缓存的判断。
Cache-control 是HTTP1.1 新增的特性,以便更精准地控制缓存。此首部信息 具有最高的优先级。
max-age 指定的是缓存的时间跨度,而非缓存失效的时间点。优先级比 Expires 高。
如果需要使用协商缓存,需要 将 Cache-control 指定为 no-cache 或者 max-age 、Expires 均过期之后。
协商缓存:浏览器本地是有缓存的,但是要先发起请求,由服务器判断缓存是否过期。
Last-Modified / If-Modified-Since
last-Modified 是 HTTP 1.0 的特性,是服务器端在响应请求时用来说明资源的最后修改时间。
缺点:
Etag / If-None-Match
Etag 是 HTTP 1.1 的特性,是服务器为资源分配的字符串形式唯一性标识,作为响应首部返回给浏览器。
采用弱比较,内容没变化,时间变化了,会认为是资源未变化。
浏览器之HTTP缓存的那些事
304和浏览器http缓存
浏览器缓存机制剖析
浏览器缓存机制介绍
技术研究 vue项目的性能优化之路
HTTP缓存控制小结
‘肆’ 前端http请求细节——Cache-Control(缓存机制)
请求和响应中的 Cache-Control 指令并不完全相同,具体可以查看 这里 ,包括指令的具体意思,这里不过多赘述。(默认值:private)
浏览器的缓存机制是根据 HTTP 报文的缓存标识进行的,浏览器第一次向服务器发起该请求后拿到请求结果,会根据响应报文中 HTTP 头的缓存标识,决定是否缓存结果。
浏览器缓存策略分为两种:强制缓存和协商缓存。
强制缓存不会向服务器发送请求,直接从缓存中读取资源,可以看到请求返回的状态码都是200,并且 Size 代表该缓存的位置。
浏览器读取缓存的顺序为memory –> disk。
三级缓存原理 (访问缓存优先级):
在浏览器中,浏览器会在js,字体,图片等文件解析执行后直接存入内存缓存中,那么当刷新页面时只需直接从内存缓存中读取(from memory cache);而css文件则会存入硬盘文件中,所以每次渲染页面都需要从硬盘读取缓存(from disk cache)。
为什么CSS会放在硬盘缓存中?
因为CSS文件加载一次就可渲染出来,我们不会频繁读取它,所以它不适合缓存到内存中,但是js之类的脚本却随时可能会执行,如果脚本在磁盘当中,我们在执行脚本的时候需要从磁盘取到内存中来,这样IO开销就很大了,有可能导致浏览器失去响应。
若服务器的资源最后被修改时间 > If-Modified-Since的字段值
则重新返回资源,状态码为200;否则则返回304,代表资源无更新,可继续使用缓存文件
If-None-Match 的字段值 = 该资源在服务器的Etag值
一致则返回304,代表资源无更新,继续使用缓存文件;不一致则重新返回资源文件,状态码为200。
ETag 和 Last-Modified 区别
参考链接:
https://juejin.im/entry/5ad86c16f265da505a77dca4
https://www.cnblogs.com/suihang/p/12855345.html
https://www.jianshu.com/p/54cc04190252