缓存一直以来都是一个老生常谈的问题,在工作和面试中都经常会被问到,合理利用了缓存可以提高网站的访问速度,节省带宽,减轻服务器压力,增强用户体验。到底我们日常工作中会用到哪些缓存呢?
- 数据库缓存:就是将查询的数据放到内存中,下次查询直接从内存中读取,提高查询效率。
- CDN缓存。
- 代理服务器缓存:浏览器和源服务器之间的中间服务器,运作原理跟HTTP缓存差不多,但是规模更大。
- 浏览器缓存:每个浏览器都实现了HTTP缓存,我们今天的重点。
- 应用层缓存。
- cookie,web storage等。
了解了我们日常生活中常用的缓存机制后(当然还有更多),今天我们重点来学习下HTTP缓存,它可是面试过程中必问的问题,想进BAT的话,必须把它啃透,废话不多说,直接进入正题。
什么是浏览器缓存?
浏览器缓存是浏览器在本地磁盘对用户最近请求过的文档进行存储,当访问者再次访问同一页面时,浏览器就可以直接从本地磁盘加载文档。
可能有同学会问是不是我们每次获取资源都必须发送HTTP请求到服务器?答案是不是地,这里就涉及到浏览器缓存的分类。
浏览器缓存的分类
浏览器缓存分为两大类:强缓存和协商缓存。
强缓存就是不需要发送HTTP请求道服务器,直接从本地磁盘获取缓存过的资源。它是利用HTTP响应报文中的Expires和Cache-Control两个字段来控制的,用来表示资源的缓存时间。
Expires:该字段是HTTP/1.0时的规范,它的值是一个绝对时间的GMT格式的时间字符串,如Expires: Mon, 06 Feb 2017 08:26:48 GMT。这个时间代表资源的失效时间,在此之间,即命中强缓存。但是它有一个明显的缺点,当客户端与服务器时间出现较大偏差,就会出现混乱。
Cache-Control:为了解决Expires出现的问题,HTTP/1.1添加了Cache-Control。主要是利用max-age来进行判断,它是一个相对时间,如Cache-Control:max-age=600,代表着资源的有效期是600秒(10分钟)。除了max-age外,Cache-Control还有以下几个常用的值:
- no-cache:不适用强缓存。需要使用缓存协商。
- no-store:禁止浏览器缓存,不适用强缓存和缓存协商,每次请求资源都需要发送HTTP到服务器,每次都需要下载完整的资源。
- public:可以被所有的用户缓存,包括客户端和CDN等中间代理服务器。
- private:只允许客户端缓存,不允许CDN等中间代理服务器对其缓存。
Cache-Control与Expires可以在服务端配置同时启用,但是Cache-Control的优先级高于Expires。
协商缓存需要由服务器来确定客户端缓存资源是否可用。这主要涉及Header中两组字段:Last-Modified/If-Modified-Since或ETag/If-None-Match,这两组字段都是成对出现的。若第一次的响应头没有Last-Modified或ETag,则后续的请求头部也不会有If-Modified-Since或If-None-Match。
Last-Modified/If-Modified-Since:浏览器第一次请求一个资源的时候,服务器返回的header中会加上Last-Modified,它是一个时间标识该资源的最后最后修改时间。当浏览器再次请求该资源时,HTTP请求头部会带上If-Modified-Since,该值为上次响应报文头部的Last-Modified的值,服务器接收到If-Modified-Since,会根据资源的最后修改时间来判断是否命中协商缓存,如果命中,返回304,并且不会返回Last-Modified和无响应body。否则返回200。
ETag/If-None-Match:它们的值不是一个时间标识,而是一个校验码。ETag可以保证每一个资源都是唯一的,资源变化都会导致ETag变化,服务器根据接收到的If-None-Match来判断是否命中协商缓存。但是当服务器返回304的时候,由于ETag重新生成过,响应头部也会带上ETag,即使它跟之前的没有变化。
为什么要有ETag?不是已经有Last-Modified吗。
- 一些文件或许会周期性的修改,但是它的内容没有变化(只是改变了修改时间),这个时候我们并不希望客户端认为这个文件修改了,而重新获取。
- 某些文件在1秒内修改了N次,用If-Modified-Since无法进行区分。
- 某些服务器不能精确的得到文件的最后修改时间。
Last-Modified和ETag可以一起使用,但是ETag的优先级大于Last-Modified,当ETag相同的情况下,才会继续比较Last-Modified,最后才决定是否返回304。
看了这么多不知道你糊涂没,下面有两张图,通过这两张图,你能对浏览器的缓存策略有一个新的认识。
第一次打开网站的时候,因为本地没有缓存,所以必须向服务器发起HTTP请求并下载所需资源,服务器返回的响应报文头部中可带相关字段来表明采取何种缓存策略。
当浏览器再次打开网站的时候,如果服务器设置了资源不可以缓存的的话(Cache-Control:no-store)则跟第一次HTTP请求一样;如果该资源可以被缓存,先判断资源是否过期,即检查Cache-Control:max-age或Expires,没过期的话,直接从本地缓存中读取,过期的话则发送一个HTTP请求到服务器,服务器根据ETag和Last-Modified来决定返回304还是200。