首页 > 学院 > 开发设计 > 正文

以 Okhttp3源码 为例 ------ 探索 缓存机制 的原理和实现(一)

2019-11-09 18:41:12
字体:
来源:转载
供稿:网友

缓存机制一直以来是一个不可忽视的重要模块,广泛地被运用到 网页端和移动端。对于服务器而言,客户端的缓存很大程度上缓解了它的压力,更是为用户带来了产品快速响应的体验,拥有很多好处。既然是网络请求,必然与HTTP协议联系紧密,不论你是否有这之类的经验,此篇将会从基础开始总结,共同学习缓存机制。


一. 初识缓存机制原理

1. 为何需要缓存?

缓存减少了冗余的数据传输,节省了网络费用。缓存缓解了网络瓶颈的问题,不需要更多的网络带宽就能更快的加载页面。缓存降低了对原始服务器的要求,服务器可以更快的响应。


2. 缓存的原理

这里写图片描述

如上图片所示,这就是浏览器的缓存原理图,以流程图的形式描绘出了大致的缓存机制。看过几遍后,便以为缓存机制也不过如此,移动端的缓存更是简单,但实际并非如此,何时采取缓存?缓存何时失效?缓存失效后的做法?等等,有许多值得思考的地方,不过有些部分浏览器已经代替实现了,所以,先来了解浏览器的缓存原理


(1)首先在有缓存的前提下,判断缓存是否过期,因为缓存相对而言会有一个存在的有效时间,如过期的话需要进一步判断是否向服务器发送请求。

(2)接着就是判断 Etag ,它是关于缓存的一个字段,每次请求都会存在的一个标识符,将文本哈希编码来标识当前文本的状态。首先会判断这个Etag是否存在,如果存在便会向服务器发送请求,在请求时会携带参数,参数If-None-Match会将Etag标记上一起发送给服务器。服务器再决策Etag是否过期,根据返回的响应码来决定从缓存读取还是请求响应。

(3)但是Etag 这个字段并不是必须存在的,当它不存在时,会再次判断 Last-Modified字段是否存在,这个字段表示响应中资源最后一次修改的时间,说白了就是服务器最新一次修改文件的时间。如果存在的话,如同Etag一样会向服务器发送请求,只是携带参数是会用If-Modified-Since去标识Last-Modified字段,一起发送给服务。在服务器决策时,会将Last-Modified与服务器修改文件的时间进行比较,若无变化则直接从缓存中读取,否则请求响应,接收新的数据。

以上内容就是浏览器的缓存机制,了解下来并非简单地判断缓存过期后,就去访问服务器,还要再次进行一系列的判断,真正确定缓存与服务器上内容不同时,需要更新时,才是去访问服务器请求最新数据。

这里,还需要强调一点,虽然缓存已经过期了,但是并非缓存与服务器的内容不同,比如服务端的数据并未做出任何更改,说明此时缓存的依旧是最新数据!所以还需要更详细的判断再来决定是否需要请求服务器更新数据,所以,避免了不必要的请求,这种缓存机制很大程度上减轻了服务器的压力!




3. 缓存相关的字段

以下字段都是在HTTP协议中的重要字段。

(1)Expires:实体主体的过期时间。

此字段最初出现于 HTTP 1.0协议,指定缓存内容的失效时间(如果该文本内容支持缓存),使用的是一个绝对值。(格林威治时间GMT标准)


(2)Cache-Control:控制缓存的行为。

此字段与Expires含义相同,那为何要存在两个含义相同的字段呢?上面有提到,Expires是一个绝对值,服务器同客户端校验的时候,有可能出现偏差,因为客户端的时间可以随意进行修改。即我们可以人为快进客户端时间,则服务器收到该时间后判断当前缓存已失效,可实际上缓存并未失效,所以这个字段就会出现一些问题。在HTTP 1.1协议出现了Cache-Control字段,它使用的是一个相对值,指令的参数有:

no-cache :无缓存指令,即每次请求直接从服务器获取。”Cache-Control”: “no-cachemax-age :代表缓存的有效时间,如果缓存只是用来和服务器做验证,可是设置更有效的”Cache-Control”:”max-age=0”。only-if-cached :先使用用缓存的数据,如果客户端有缓存,会立即显示客户端的缓存,这样你的应用程序可以在等待最新的数据下载的时候显示一些东西, 重定向request到本地缓存资源,添加”Cache-Control”:”only-if-cached”。max-stale :即使缓存已过期,也可先展示出来。有时候过期的response比没有response更好,设置最长过期时间来允许过期的response响应: int maxStale = 60 * 60 * 24 * 28; // tolerate 4-weeks stale “Cache-Control”:”max-stale=” + maxStale。

(3)Last-Modified:资源的最后修改日期时间。

响应中资源最后一次修改的时间,用于判断服务器和客户端资源是否一致的重要字段


(4)ETag:资源的匹配信息。

也是用于判断服务器和客户端资源是否一致的重要字段响应中资源的校验值,为何会存在相同意义的字段?因为如果你在服务器端修改资源后,Last-Modified会改变,可是此时客户端与服务器端资源是否一定不同呢?也许你只是多加了一个空格,此时Last-Modified的改变意味着缓存已失效,可这样请求服务器获取到的数据却是相同的。所以 HTTP协议推出了ETag 它将服务器返回的 response整个编码处理加密得到的一个值,在服务器上某个时段是唯一标识的,将此值与客户端缓存中的ETag进行比较,可避免Last-Modified漏掉的问题。主旨在于比较两者的内容是否发生变化,而不是单纯的比较时间。


(5)Date:创建报文的时间。

服务器创建报文时的时间。


(6)If-Modified-Since:比较资源的更新时间。

(判断缓存是否失效时标识在请求头的标识量)客户端存取的该资源最后一次修改的时间,同Last-Modified


(7)If-None-Match:比较实体标记。

(判断缓存是否失效时标识在请求头的标识量)客户端存取的该资源的检验值,同ETag




4. 举例熟悉HTTP 字段

这里写图片描述 这里写图片描述

以上文件是腾讯网的一个js文件,从右侧可以得知:

Cache-Control:它支持缓存,缓存时间为259200秒,也就是三天。Date:表示资源发送的时间,指的是服务器的时间,与当时请求时间并非相同!Expires:资源过期的时间,用Date加上缓存有效时间 max-age而得。Last-Modified :最后一次修改的时间。Etag:改js文件被编码加密后得到的一个标识值。




二. 了解 Okhttp3网络框架 缓存功能

以上讲解的部分,已经初始了缓存机制的原理,接下来先通过一个小例子来见识 Okhttp3网络框架的缓存机制。

1. 例子测试缓存功能

public class CacheHttp { public static void main(String args[]) throws IOException { int maxCacheSize = 10 * 1024 * 1024; //缓存大小的限制:10M //创建cache 类,需要两个参数(文件目录,文件大小) Cache cache = new Cache(new File("/Users/gym/source"), maxCacheSize); OkHttpClient client = new OkHttpClient.Builder().cache(cache).build(); Request request = new Request.Builder().url("http://www.QQ.com/"). cacheControl(new CacheControl.Builder().maxStale(365, TimeUnit.DAYS).build()). build(); Response response = client.newCall(request).execute(); String body1 = response.body().string(); System.out.PRintln("network response " + response.networkResponse()); System.out.println("cache response " + response.cacheResponse()); System.out.println("**************************"); Response response1 = client.newCall(request).execute(); String body2 = response1.body().string(); System.out.println("network response " + response1.networkResponse()); System.out.println("cache response " + response1.cacheResponse()); }}

关于以上代码需要注意的是:response请求资源后的相应有两个相关方法,networkResponse()cacheResponse()。从字面意义上理解,一个是从网络请求中获取资源,另一个是从缓存中获取资源。如果该资源是从服务器获取的,networkResponse()返回值不会为null,即cacheResponse()返回值为null;如果是从缓存中获取的,networkResponse()返回值为null,cacheResponse()返回值不为null。



这里写图片描述

以上截图就是测试后的结果:因为腾讯网是默认有缓存的,所以我在第一次请求网络时,电脑中并无缓存,资源从服务器上获取,cacheResponse()返回值为null。第二次重复请求腾讯网,自然是从缓存中获取资源。




2. 剖析缓存文件

从以上的小例子可得知Okhttp3网络框架的缓存功能,再更详细了解它的缓存,我在代码中指定了将缓存放到文件夹中,在两次请求网络过后,文件夹多出了三个文件,如下图所示: 这里写图片描述


(1)7f4c79817fabaeaa0e909754cfe655e7.0 文件

这里写图片描述

了解了HTTP协议中的字段后,发现这个内容很是熟悉,是请求腾讯网后返回的response的一些数据。比如说请求的url、请求方式、HTTP协议、返回码,下面的就是腾讯网响应头的数据,不妨来看看哪些与缓存相关?

Cache-Control:腾讯网支持的缓存有效时间为60秒。Date:请求资源时的服务器时间(注意!该时间与客户端的时间是不对应的)。Expires:缓存失效时间,即Date加上缓存有效时间max-age

(2)7f4c79817fabaeaa0e909754cfe655e7.1 文件

了解了一些基本数据后,腾讯网具体资源展示是在第二个文件,由于全部都是二进制数据,这里就不截图展示了。

其实观察前两个文件名,很相似却又有些不一样,其实这是根据腾讯网的url加密后所得。



(3)journal 文件

这里写图片描述

此文件用于 Okhttp 缓存读取目录时会用到的,可以查看当前客户端请求网络的次数和具体调用请求的地方。看第一行数据,这个也是DiskLruCache所要用到的目录结构。




第一篇是介绍总结一下缓存机制有关的原理和Ohttp3网络框架有关的功能,先铺垫一下基础。第二篇正式从解析源码

这里正式感谢nate老师对此的讲解,我才得以总结学习,thx~

希望对你们有帮助 :)


发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表