1.缓存的优点

  1) 缓存减少了冗余的数据传输,节省了网络费用。

  2) 缓存缓解了网络瓶颈的问题,不需要更多的网络带宽就能更快的加载页面。

  3) 缓存降低了对原始服务器的要求,服务器可以更快的响应。

2.缓存分类

  1)私有缓存

  常见就是我们的浏览器里内置的缓存。

  2)公有缓存

  常见的就是代理缓存,不多介绍。

3.缓存的处理流程

这里写图片描述
处理流程图,如上所示,下面分步骤具体介绍:

  1)请求处理

  用户发起一个http请求,缓存获取到URL,根据URL查找是否有匹配的副本,这个副本可能在内存中,也可能在本地磁盘。

  2) 新鲜度检测

  如果缓存中存在所请求资源的副本,则进行新鲜度检测。新鲜度检测举个简单的例子,我们在商店买了一瓶汽水,汽水瓶上肯定会标有过期时间,我们会根据这个过期时间和现在的时间做对比,看看饮料过期了没,如果没过期,我们正常喝就行了,如果已经过期,我们肯定要找商家。。。其实这就是一个新鲜度检测的过程,HTTP请求的新鲜度检测流程也是这样的,HTTP发起一个请求时,发现缓存中有相应的副本,接着就会检查这个副本有没有过期,如果没有过期,直接使用。如果已经过期,则进行再验证。具体的实现在下面会介绍。

  3)服务器再验证(缓存过期是前提条件)

  缓存中的文档过期了并不代表他和服务器上的不一样,所以这个时候就需要问问服务器,过期的这段时间里这个文档到底有没有改变。如果改变了,缓存就会获取一份新的文档副本,然后发送给客户端。如果没有改变,缓存只需要获取新的首部,包括一个新的过期时间,并对缓存中的首部更新。

  4)创建响应并返回

  我们希望缓存看起来就像是来自原始服务器一样,缓存将已缓存的服务器响应首部作为响应首部,发送给客户端。

4.保质期的实现

  HTTP中,通过Cache-Control首部和Expires首部为文档指定了过期时间,通过对过期时间的判断,缓存就可以知道文档是不是在保质期内。Expires首部和Cache-Control:max-age首部都是来告诉缓存文档有没有过期,为什么需要两个响应首部来做这件简单的事情了?其实这一切都是历史原因,Expires首部是HTTP 1.0中提出来的,因为他使用的是绝对日期,如果服务端和客户端时钟不同步的话(实际上这种情况非常常见),缓存可能就会认为文档已经过了保质期。

  HTTP 1.1为了修正这个问题,引入了Cache-Control:max-age首部,这个首部使用相对时间来控制保质期,让一切变得更加合理。举个例子,我们买了一瓶汽水,如果使用Expires首部来标注保质期,就会这么写:饮料过期时间:2012年12月21日,如果某个2货不知道今天多少号,他还真不知道这饮料过期没,我小时候饮料都这么写。后来,有个挺有名的卖牛奶的,大概就叫蒙牛,他发明了一种标注保质期的方法,他怎么搞了?他这么写:保质期:12个月,行,牛逼了,我牛奶一年前就生产出来的牛奶,今天要发给厂家,发之前,先往包装上印上生产日期(当然是印发货那天),然后告诉你,明年才过期,这多聪明,搞成相对的,毒死你。也许HTTP 1.1借鉴了这个伟大的发明,于是就有了Cache-Control:max-age首部。

5.服务器再验证的实现

  缓存要问问服务器,牛奶已经过期了,到底还能不能喝。我说错了,是文档,不是牛奶。HTTP中,使用两个请求请首部来完成这个功能:If-Modified-SiceIf-None-Match。为啥又要两个首部来完成这个功能了?答案还是因为历史的原因。一开始使用 If-Modified-Sice:<date>首部,date是上一次缓存牛奶时,响应中Last-Modified首部的值。

  客户端拿着这个值,问服务器,这段时间内这个牛奶你有没有修改过?服务器看了看这个牛奶的修改时间,如果没有修改过,会返回一个304 Not Modified的响应;如果修改过,把最新的牛奶返回给客户端。后来,人们发现这样有问题,因为就算修改时间变化了,文档也不一定发生改变!于是乎,就有了 If-None-Match:<tag>首部,tag是上一次缓存文档时,响应中Etag的值,Etag是一种唯一标识资源的方式,就像java中的hashcode,如果hashcode不一样,那么两个对象肯定不一样!

6.试探性过期

  如果响应中既没有Cache-Control:max-age首部又没有Expires首部,缓存可以计算出一个试探性最大使用期。这东西打个比方就是缓存会根据响应的Last-Modified来决定这文档靠不靠谱,需不需要再验证,如果Last-Modified中的日期是很早之前,那缓存就认为这文档挺靠谱,近期之内应该不会变化;如果Last-Modified中的日期是最近几天,那缓存可能就认为这文档可能经常改变,不靠谱。当然这么粗略的判断想想就知道不严谨,所以我们一定要设置Expires首部和Cache-Control首部。

7.写在最后

  如果你是个好学者,看完这个文章,你可能会迫不及待的打开你的chrome浏览器,F12,观察oschina的HTTP信息。这个时候的你,肯定和几个月前的我一样茫然,因为你发现我上面讲的很多东西再这里被现实无情推翻了。看看这个讨论记录,这是几个月前我在OSC上问的。之所以会出现这个情况,是因为chrome为了保证牛奶的安全放心,所以他不太信任包装上的保质期标识,每次都问问服务器(再验证),牛奶有没有变化,没变化的话他就直接喝了!一切都是为了安全!


Http缓存机制

什么是Http缓存

缓存缓存,就是把需要的东西存起来,不需要每次都去请求。主要目的减小服务器压力,放到客户端上来讲,还利于节省流量,还能流畅的把UI显示出来,提高了用户体验。对于Http缓存来讲,主要的就是校验缓存的有效性,也就是新鲜度。如果客户端不能及时响应服务端的数据变化,缓存一直不能被更新,那不就是得不偿失了?

Http缓存策略

1.Expires策略

Expires是Web服务器响应消息头字段,在响应http请求时告诉浏览器在过期时间前浏览器可以直接从浏览器缓存取数据,而无需再次请求。Expires规定了缓存失效时间,客户端拿当前时间和这个失效时间比较,如果超过了这个时间,即无效。

Expires策略有2个明显的缺点:
1.缓存时间依赖于客户端,每个客户端的时间可能不相同,精度不高。
2.缓存有效期的时间精度不高。

举个例子,一个新闻列表的缓存时间是有后台运营决定的,当我产生这个列表的时候是无法确定该列表到底多久以后会被更新,只能预估一个时间。只有当列表被更新,我才知道这个更新时间之前的资源都是过期的。

而且,Expires是HTTP1.0的东西,而Cache-Control是HTTP1.1的,规定如果max-age和Expires同时存在,前者优先级高于后者

2.Cache-control策略

Cache -Control指定请求和响应遵循的缓存机制。

响应消息中的指令包括

  1. Public指示响应可被任何缓存区缓存。
  2. Private指示对于单个用户的整个或部分响应消息,不能被共享缓存处理。这允许服务器仅仅描述当用户的部分响应消息,此响应消息对于其他用户的请求无效。
  3. no-cache指示请求或响应消息不能缓存
  4. no-store用于防止重要的信息被无意的发布。在请求消息中发送将使得请求和响应消息都不使用缓存。
  5. max-age指示客户机可以接收生存期不大于指定时间(以秒为单位)的响应。
  6. min-fresh指示客户机可以接收响应时间小于当前时间加上指定时间的响应。
  7. max-stale指示客户机可以接收超出超时期间的响应消息。如果指定max-stale消息的值,那么客户机可以接收超出超时期指定值之内的响应消息。
    ##3.缓存校验字段

Last-Modified/If-Modified-Since

Last-Modified:标示这个响应资源的最后修改时间。web服务器在响应请求时,告诉客户端资源的最后修改时间。

If-Modified-Since:当资源过期时(使用Cache-Control标识的max-age),发现资源具有Last-Modified声明,则再次向web服务器请求时带上头 If-Modified-Since,表示请求时间。web服务器收到请求后发现有头If-Modified-Since 则与被请求资源的最后修改时间进行比对。若最后修改时间较新,说明资源又被改动过,则响应整片资源内容(写在响应消息包体内),HTTP 200;若最后修改时间较旧,说明资源无新修改,则响应HTTP 304 (无需包体,节省浏览),告知浏览器继续使用所保存的cache。

Etag/If-None-Match

Etag:web服务器响应请求时,告诉浏览器当前资源在服务器的唯一标识(生成规则由服务器觉得)。Apache中,ETag的值,默认是对文件的索引节(INode),大小(Size)和最后修改时间(MTime)进行Hash后得到的。

If-None-Match:当资源过期时(使用Cache-Control标识的max-age),发现资源具有Etage声明,则再次向web服务器请求时带上头If-None-Match (Etag的值)。web服务器收到请求后发现有头If-None-Match 则与被请求资源的相应校验串进行比对,决定返回200或304。

请求流程

当第一次发起请求时:
这里写图片描述

第二次请求:
这里写图片描述


本文转载:CSDN博客