缓存与浏览器缓存技术:
一,缓存:
缓存是网络传输中常用到的一种技术,利用缓存可以让我们在数据传输方面更加的方便和快捷。
1.1缓存的优点:
-
避免冗余的数据传输:
当很多的人去访问一个网站的原始服务器,此时原始服务器会为每个访问着者都发送一份相同的文本。这样就会浪费我们的网络带宽,同时使我们的原始服务器负载加重。假如使用了缓存,此时我们就可以在缓存服务器中备份一份文本,此时再有访问者访问就不用直接和原始服务器对接,让缓存去发送文本。这样可以减少我们的原始服务器的负载压力,同时可以避免一些不必要的数据传输。
-
缓解带宽瓶颈:
缓存还可以缓解带宽瓶颈。我们都知道在网络传输中,数据的传输速率会以路径中最小的的带宽来计算。而大多数的网络一般为本地网络提供的带宽是大于远程网络的带宽。当我们在本地做了缓存之后就不用远程的去向原始服务器请求数据,则避免了远距离传输数据时受到小带宽的限制,这样我们就利用局域网的较大带宽来传输文本。
-
避免瞬间拥塞:
瞬间拥塞问题其实很考验原始服务器的抗压能力,当在网络上有一个热点问题的时候,会在短时间内有巨大的点击量,这样服务器不得不在但时间内处理百万计的网络请求。如果我们不做缓存处理,那么这样的对原始服务器的考验是巨大的。如果我们做了缓存,用户没有必要向服务器请求一些非必要的数据或者文本,这样对于原始服务器来说是一个极大的帮助,减少了服务器去接收访问的数据的次数。
-
距离延迟:
数据传输的过程中,当发送者和接收者两者的距离很远,那么数据传输带来的延迟就越明显。即使是光速传输,也会存在延迟,而缓存能够让远距离数据传输变得很低。
二,命中和未命中:
可以用缓存服务器中已有的副本来为客户端的请求提供服务,这种行为称为缓存命中,其实缓存命中的简单理解就是客户端从缓存中拿到了它想要的数据。反之被称作缓存未命中。
2.1再验证:
当我们的缓存服务器缓存原始服务器发来的数据时,经过一段时间后,原始服务器的内容可能会因为某些需求而发生改变。缓存要不定时的对缓存的数据进行检测,来确定我缓存的数据是不是最新的数据。这些新鲜度检测被成为HTTP再验证。为了有效的进行在验证,HTTP定义了一些特殊的请求,不用从服务器上获取整个对象就可以检测出是否是最新数据。
通常情况下,缓存服务器并不会去主动的去向原始服务器发送请求来验证数据是否是最新的,原因在于这样会消耗一定的带宽,假如一个缓存服务器中有百万量级的存储文本,那么去验证一次需要消耗大量的资源。那么什么时候采取验证呢,一般情况下是用户发起请求的时候缓存服务器去向原始服务器发送验证请求。其实这里注意一下,并不是只要用户发起请求缓存服务器就发送验证请求的,是有前提条件的。这种前提条件我们后面会详细的讲到。
缓存服务器在对缓存进行验证的时候会向原始服务器发送一个小小的请求,这个请求的数据量其实是很小的。原始服务器会根据现有的数据来对请求进行回应。
HTTP为我们准备了几个用于再验证的工具,后面将会讲到,而最常用的是If-Modefied-Since首部,该首部的值是一个日期,该日期是该文件最后更改的日期。原始服务器会对请求进行回应,通常有以下三种情况:
-
再验证命中:
如果服务器文件未被修改,服务器会以一个
304 Not Modified进行响应。只要缓存知道文本有效,那么就暂时将原来存储的副本标记为有效,并将副本提供给客户端,这个过程被称为:再验证命中。 -
再验证未命中:
如果原始服务器的数据已经改变,此时原始服务器会把最新的文本发送给客户端,然后返回
200 OK,然后缓存服务器会拷贝一份最新的文本进行缓存。 -
文本被删除:
当我们再验证的文本已经被删除,服务器会返回
404 Not Found进行响应。缓存服务器也会将其副本删除。
2.2区分命中和未命中的情况:
通常情况下,我们仅凭借状态码是无法分清我们请求的数据是否是命中或者未命中。也就是说,我们并不知道我们的获取的数据是来自缓存服务器还是原始服务器。因为我们获取到数据后的状态码都是200 OK。其实我们可以通过其他的响应头来判别该数据是从缓存中获取还是原始服务器。比如:Last-Modified字段,如果该字段的值比较早,那么就是从缓存中获取。
三,缓存的拓扑结构:
缓存的种类一共有两种:私有缓存和公有缓存。
3.1私有缓存:
其实私有缓存是一对一的关系,也就是为一个用户来服务。私有缓存不需要很大的存储空间。其实浏览器内部的缓存就是私有缓存。大多数的浏览器有自己内建的私有缓存,浏览器通常会把常用的文档缓存到我们的个人电脑的磁盘和内存当中。
3.2公有缓存:
公有缓存是特殊的共享代理服务器,被称为代理缓存。代理缓存的一大特点是它与用户的关系是一对多的,公有缓存一般是为一个集体服务的,把这个集体经常访问的数据缓存到公有缓存当中,这样的好处是公有缓存能够提供给多个用户的请求,可以减少冗余的流量。和私有缓存相比,公有缓存可以减少更多的再验证请求,比如要验证一个数据的新鲜度。对于私有缓存来说,每次都要向原始服务器发送验证请求,有100个用户就要发送一百个请求。而对于公有缓存来说,只要有一个数据过期,那么只需要验证一次,然后将最新的数据缓存到缓存服务器当中,当其他的用户去请求这个资源的时候可以直接从缓存服务器当中来获取最新的数据,不用再经行验证了,原因就在于别人已经让缓存服务器验证过了。
四,缓存的处理步骤:
我们处理缓存的时候大致可以分为七步:接收,解析,查询,新鲜度检验,创建响应,发送,日志。接下来我们细说每一步在缓存中扮演的角色。
4.1接收:
通常情况下,当我们向一个站点发送一个资源请求的时候,会先向缓存服务器发该资源的资源请求,缓存服务器会从网络中读取抵达缓存服务器的资源请求报文。
4.2解析:
当缓存服务器接收到报文后,缓存将请求报文解析成片段,将首部的各个部分放入易于存储的数据结构中,这样缓存就可以更容易处理首部字段并修改它们了。
4.3查找:
在这一步中,缓存会获取到URL,此时缓存会在缓存服务器中查找该数据。查询的结果通常有两种,一是缓存服务器以前缓存过该数据。二是缓存服务器里并没有这个数据。当缓存服务器没有该数据的时候会向原始服务器发送请求,该请求的报文是在客户端的请求报文基础上的改进。然后原始服务器会将请求的数据返回给缓存服务器,缓存服务器将该数据复制一份然后保存,并保存原始服务器的响应报文。如果缓存服务器中有该数据,则接下来对其进行新鲜度检测。
4.4新鲜度检测:
HTTP可以通过一些响应头字段将发送给缓存服务器的文档在缓存服务器保存一段时间,在这段时间内,我们可以认为该文档副本是新鲜的,缓存服务器可以在不联系原始服务器的情况下向客户端直接提供该文档。但是一旦过了这段时间,我们就认为该文档是不新鲜的,当客户端去请求该文档的时候,缓存服务器必须向原始服务器发送再验证请求。
4.5创建响应:
我们希望缓存服务器的响应看起来就像是原始服务器的响应一样,所以缓存服务器会将原始服务器的响应缓存到自己的内存当中。缓存服务器在向客户端进行响应的时候,它会在创建响应的时候会将原始服务器的响应首部作为缓存服务器响应首部的起点,然后对这些基础首部进行了修改和扩建。
4.6发送:
一旦响应首部准备好了,缓存服务器就将响应报文和数据返回给客户端。
4.7日志:
大都数缓存都会保存日志文件以及与缓存相关的一些统计数据。
4.8缓存处理流程图:
五,如何让备份数据在缓存服务器中保存一段时间:
5.1文档过期:
通过HTTP的Cache-Control和Expires,HTTP让原始服务器向每一个文档都添加了一个"过期时间",可以比作商品的保质期。在这段时间内我们可以认为文档是新鲜的,在文档过期之间,缓存服务器可以任意的使用这些副本为不需要与服务器联系,但是假如客户端必须让缓存服务器与原始服务器联系就另当别论了。
5.2过期时间和使用期:
在HTTP协议中 ,我们可以使用Cache-Control和Expires来为一个资源设置它在缓存服务器中的新鲜度保存时间,两者的本质是一样的,都是来设置时间。但是在形式上是不同。
-
Cache-Control:格式:Cache-Control : max-age = 3600,该字段的值的单位是秒,max-age定义了文档最大的使用期限。从第一次生成文档到文档不在新鲜,无法使用为止。 -
Expires:这个响应头的字段就比较简单了,就是指定一个过期的时间。当超过了指定的时间就认为该文档不新鲜了。
在实际的应用当中,使用最多的是Cache-Control。当我们设置了文档的过期时间后,客户端对该数据的请求就由缓存服务器来处理,不用通知原始服务器。
5.3服务器再验证:
可能有人会有疑问,当我们的文档假如过期之后会怎么办呢。当客户端向缓存服务器去请求文档时,发现该文档的保质期已经过了,文档的保质期过了并不代表他不能再用了,时间过期只是代表该到进行文档核对的时间了,此时它会向服务器发送再验证请求进行文档的再验证。
- 如果再验证显示内容发生了变化,缓存会获取一份新的文档副本,并将其储存在旧文本的位置上,然后将文本发送给客户端。当然这个发送的过程包括了我们说的响应的创建和发送等细节。
- 如果再验证显示内容并没有发生变化,那么缓存只获取新的首部,包括一个新的过期日期,然后将自己缓存的文案发给客户端连同缓存修改和扩充过的响应头。
六,如何进行再验证:
我们以浏览器为例,其实再验证是浏览器内部自己实现的内置功能,不需要我们去进行上面所讲的一系列操作,比如缓存处理的七个步骤,新鲜度的检查以及我们现在要说的再验证。这是针对浏览器来说的。可能其他设备有所不同。那么浏览器是如何实现再验证的呢?
用条件方法进行再验证。HTTP允许向原始服务器发送一个条件GET。我们向get请求报文中添加一些特殊的条件首部就可以发起条件GET。
HTTP定义了5个条件请求首部。对缓存再验证来说最有用的有两个:If-Modified-Since和If-None-Match。每一个条件首部对应着一个再验证机制。
6.1 If-Modified-Since/Last-Modified再验证:
If-Modified-Since和Last-Modified是配套使用的。当浏览器发送一个条件GET时,该请求报文首部有一个If-Modified-Since字段,而这个字段是文档最后更改的日期,当原始服务器接收之后,提取If-Modified-Since字段的值,然后和Last-Modified的值进行对比。假如时间不一致说明原始服务器中的数据已经被修改过了,此时会返回一个最新的文档和一个响应报文,响应首部的状态码为:200 OK,其中还包括一个新的文档过期时间和最近一次文档修改的时间(即Last-Modified字段),而这个修改的时间就是下一次浏览器缓存再发一个条件GET时If-Modified-Since的值。当接收到新的文档时浏览器缓存会备份该文档,然后对收到的原始服务器的响应首部进行修改和扩充发送给客户端。假如对比If-Modified-Since和Last-Modified一致,说明原始服务器中的数据并没有修改过,此时服务器会返回304 Not Modified,还有一个新的过期时间。**这里原始服务器只会返回响应头,不会返回响应体。**当浏览器缓存接收到该响应时会将以前缓存的文档发送给客户端,并向客户端发送304 Not Modified,表示并没有从原始的服务器中获取数据。这里注意以下,这里返回的是304 Not Modified而不是200 OK,后者出现的第一种情况是出现文档还在保质期内,直接从缓存中获取。第二种的情况是文档保质期过了,并且文档被修改过,然后向原始服务器要最新的文档。
6.2 If-None-Match实体标签再验证:
If-None-Match和ETag搭配着使用。其实和If-Modified-Since/Last-Modified再验证的思想一样,都是通过比对来判断文档是否已经被修改过。其实ETag本质上相当于一个字符串表示符,你可一把他赋值为一串数字字符串来代表现在这个文档的版本号,当文档被改变的时候可以修改ETag来表示文档已经更新。当浏览器使用If-None-Match来进行再验证的时候,服务器会获取If-None-Match的值来与ETag进行对比。如果内容一致,说明文档并没有发生改变,然后返回304 Not Modefied,还有一个新的过期时间,浏览器缓存的动作与``If-Modified-Since的动作一致。对比后发现如果不一致说明文档被修改过了,此时会返回一个最新的文档和一个新的过期时间。浏览器缓存的动作和上面再验证的动作一样。也是先将文档缓存,然后返回给客户端,并返回304 Not Modified`。
6.3注意:
我们要把If-Modified-Since/Last-Modified,If-None-Match/ETag与Cache-Control/Expires区分开来。Cache-Control/Expires是用来设置过期时间的。而If-Modified-Since/Last-Modified,If-None-Match/ETag是用来当过期时间到了之后进行再验证的。
七,浏览器缓存:
其实当你了解了缓存机制之后就不在对浏览器缓存感到陌生了,本质上浏览器缓存和服务器缓存(或者叫做缓存服务器本质上是一样的)。只不过浏览器厂商在浏览器内部实现了自己的缓存机制,可以看作一个小型的缓存服务器。
7.1浏览器缓存的分类:
浏览器缓存可以分为两种:强缓存和协商缓存。可能这个时候就有同学有疑问了,不是说好了和缓存相似吗,为什么这两种缓存没有见过呢。我们往后讲就知道了。
7.1.1强缓存(Expires&Cache-control):
- 当浏览器对于某个资源的请求命中了强缓存,返回的http状态码为200,在chrome的开发者工具的network面板里面size会显示from cache。
- 强缓存是利用
Expires和Cache-Control这两个请求头字段来实现的,它们都表示资源在客户端(这里指的是浏览器缓存)的有效期。 -
Expires表示的是一个资源过期的时间的请求头字段,它描述的是一个绝对的时间,由服务器返回,GMT格式来表示,在这段时间浏览器不用向原始服务器发送任何请求。 -
Expires缓存原理如下:- 浏览器第一次跟服务器请求一个资源,服务器在返回这个资源的同时,会在响应头上添加
Expires。(其实浏览器会先向浏览器缓存发送请求)。 - 浏览器在接收到这个资源后会把这个资源连同所有的响应头一起缓存下来,所以强缓存命中的响应头并不是来自原始服务器,而是来自浏览器缓存中的响应头。
- 浏览器再一次请求这个资源的时候,先从缓存中寻找,找到这个资源后,拿出
Expires的值与现在的时间对比,如果在Expires的时间保质期内,那就命中强缓存,否则没有命中缓存。
- 浏览器第一次跟服务器请求一个资源,服务器在返回这个资源的同时,会在响应头上添加
-
Expiress是一个比较古老的技术,而现在使用的是Cache-Control,这是一个相对时间,它的值是数值,表示资源有效期的秒数。浏览器在对待Cache-Control的行为和Expires是一样的。
7.1.2协商缓存(Last-Modified/If-Modified-Since&ETag/If-None-Match):
- 当浏览器对某个资源没有命中强缓存的时候,就会发送一个请求到服务器,验证协商缓存是否命中,如果协商缓存命中,响应头返回的
http状态码为304,并且会显示一个Not Modified的字符串。 - 协商缓存是是利用
[If-Modified-Since/Last-Modified],[If-None-Match/ETag]这两个请求响应头来管理的。 - 协商缓存和强缓存不一样,强缓存不发请求到服务器,所以有时候资源更新了浏览器并不知道,但是协商缓存会发请求到原始服务器,所以资源是否更新,浏览器也可以知道。
八,对比:
其实我们通过上面对浏览器缓存的描述,我们可以发现,其实浏览器缓存中的两个缓存机制是和我们普通的缓存是有很大关系的。强缓存的本质就是文档没有过保质期时缓存服务器和客户端的一些列动作。而协商缓存就是缓存服务器再验证的过程。其实浏览器缓存本身可以看作是一个缓存服务器,只不过它内嵌在浏览器当中了。缓存服务器的绝大多数特性都可以适用于浏览器缓存。