如果让我们去实现一个HTTP缓存机制,我们应该怎么去实现呢,接下来我们将带着这个去阅读这篇文章!
思路1,无缓存
假设浏览器向服务器请求资源a.html时,服务器找到对应的资源a.html,然后将资源返回给浏览器。当浏览器再次向服务器请求该资源时,服务器将再次将该资源返回给浏览器。
优点:简单。。
缺点:每次请求资源都需要服务器查找,然后再返回资源,导致服务器相应慢,浪费带宽。
思路2,有缓存无更新
我们通过思路1的思考,忽然大悟!哦,原来每次请求资源会导致这样的问题啊,那么我们加个缓存不就好了吗?哈哈,我真是太天才了。因此接下来,浏览器第一次请求资源a.html时,服务器会响应并发送完整文件,然后浏览器可以把这个文件存到本地(缓存),下次浏览器再次请求该资源时,就直接读取本地缓存就可以,这应就能提高网页响应速度和省下带宽了。
优点:省带宽,提高页面响应速度。
缺点:果服务器上a.html的文件内容变了,浏览器每次都从缓存读取无法获取最新文件
思路3,缓存+更新机制
我们接下来思考!唔,思路二还是存在重大缺点,得去改进它。接下浏览器在请求a.html时服务器会发送完整的文件,并且服务器发送的文件还附带额外的信息——过期时间,如** Expires: Mon,10 Dec 1990 02:25:22GMT**,浏览器可以把这个文件和额外信息存到本地。当浏览器再次想服务器请求a.html时,浏览器就会用当前的时间和Expires的时间作比较,如果还在Expires规定的时间内,则会使用本地缓存的a.html(200, from xx cache);否则再次向服务器再次请求a.html(200)。 服务器在每次给资源的时候都会发送新的过期时间。
优点:缓存可控制
缺点:控制的功能太单一,很有可能服务器a.htm更新了,客户端还在用旧的资源。
思路4,缓存+更新机制升级版
上面我们已经实现了简单的缓存的更新机制。但是还是觉得不太智能,功能太单一,我们思考一下,看一下应该如何完善。HTTP1.1版本提供了Cache-Control让我们更好地去控制缓存。
比如:浏览器第一次请求a.html 时,服务器会发送完整的文件并附带额外信息。
Cache-Control: max-age=300;
浏览器把文件和附带信息保存起来。当再次需要a.html 时,如果是在300秒以内发起的请求则直接使用缓存(200, from xx cache),否则重新发起网络请求(200)。(和Expires有点类似),下面是Cache-Control常见的几个值:
<li>Public表示相应可以被任何中间节点缓存,如 Browser <-- proxy1 <-- proxy2 <-- Server,中间的代理可以缓存资源,比如下次再请求同一资源proxy1直接把自己缓存的东西给 Browser 而不再向proxy2要。</li>
<li>Private表示中间节点不能缓存,对于Browser <-- proxy1 <-- proxy2 <-- Server,proxy2 会老老实实把Server 返回的数据发送给proxy1,proxy1 会老老实实把Server 返回的数据发送给Browser ,自己不缓存任何数据。当下次Browser再次请求时proxy会做好请求转发而不是自作主张给自己缓存的数据。</li>
<li>no-catch表示不使用 Cache-Control的缓存控制方式做前置验证,而是使用 Etag 或者Last-Modified字段来控制缓存 </li>
<li>no-store ,真正的不缓存任何东西。浏览器会直接向服务器请求原始文件,并且请求中不附带 Etag 参数(服务器认为是新请求)。</li>
<li>max-age,表示当前资源的有效时间,单位为秒。</li>
优点:缓存功能更为强大。
缺点:加入浏览器再次请求a.html,而缓存存在的时间间隔超过max-age设置的,这时候向服务器发送请求服务器应该会重新返回a.html的完整文件。假如,请求的a.html和原来的a.html一样,没有任何改变,则这就浪费了带宽了。
思路5,缓存+更新机制终极版
鉴于思路4的实现方式还是有瑕疵,因此我们在思考一下。
比如:浏览器第一次请求a.html的时候,服务器响应发送完整的文件并附带额外信息,其中Etag 是 对a.html文件的编码,如果服务端的a.html没有修改,则这个值就不会变。
Cache-Control: max-age=300;
ETag:W/"e-cbxLFQW5zapn79tQwb/g6Q"
浏览器把a.html和额外信息存放在本地,当缓存在max-age设置的时间间隔内,则直接读取缓存a.html(200, from xx cache),假如浏览器在300秒之后再次需要获取a.html时,浏览器发现该缓存的文件已经不新鲜了于是就向服务器发送请求 重新获取a.html,在发送请求的时候附带刚刚保存的a.html的ETag ( If-None-Match:W/"e-cbxLFQW5zapn79tQwb/g6Q"),服务器在接收到请求后拿浏览器请求的 Etag 和当前文件重新计算后端 Etag 做个比较,如果二者相等表示文件未修改则发送这个短消息(响应头,不包含图片内容, 304),如果二者不等则发送新文件和新的 ETag,浏览器获取新文件并更新该文件的 Etag。
另外与Etag相似的是Last-Modified/If-Modified-Since。当资源过期时(max-age超时),发现资源具有Last-Modified声明,则再次向web服务器请求时带上头 If-Modified-Since,表示请求时间。web服务器收到请求后发现有头If-Modified-Since 则与被请求资源的最后修改时间进行比对。若最后修改时间较新,说明资源又被改动过,则响应整片资源内容(200),若最后修改时间较旧,说明资源无新修改,则响应HTTP 304 ,告知浏览器继续使用所保存的cache。
总结
经过带着问题,不断提出新的思路,我们最终实现控制缓存的比较好的方式。其实HTTP对于前端是非常重要,尤其当我们学习了Node.js或者其他后端语言去写web服务端的时候,我们发现离不开HTTP,因此我们应该要注重HTTP的知识,而不只是比较这个框架好,还是那个框架牛。况且框架的使用视业务场景而定。
网友评论