缓存(cache)是计算机领域的一个重要概念,是优化系统性能的利器。
由于链路漫长,网络时延不可控,浏览器使用HTTP获取资源的成本较高。所以,非常有必要把”来之不易“的数据缓存起来,下次再请求的时候尽可能复用。这样可以避免多次请求-应答的通信成本,解压网络带宽,加快响应速度。
实际上,HTTP传输的每一个环节基本上都会有缓存,非常复杂。基于”请求—应答“模式的特点,可以大致分为客户端缓存和服务器缓存。
一、客户端缓存(浏览器缓存)
HTTP流程:
浏览器发现缓存无数据,于是发送请求,向服务器获取资源→服务器响应请求,返回资源,同时标记资源的有效期(服务器用”Cache-Control:max-age = 30“标记缓存的有效期,”这个页面只能缓存30s,之后就过期不能用“)→浏览器缓存资源,等待下次重用。
之所以要给资源加有效期是因为网络上的数据随时可能会发生变化,不能保证它稍后的一段时间还是这样,其中max-age即”生存时间“,是从服务器响应报文开始计算,而不是客户端收到报文开始,也就是包含了在链路传输中所以节点所停留的时间。max-age是HTTP缓存控制最常用的属性,此外在响应报文里还可以用其他的属性来更精确的指示浏览器如何使用缓存:
no-store: 不允许缓存,用于某些变化非常频繁的数据,如秒杀页面;
no-cache: 可以缓存,但是使用之前必须去服务器验证是否过期,是否有最新的版本;
must-revalidate: 如果缓存不过期就可以用,过期了还想用就要去服务器验证;
缓存什么时候可以用:当我们进行”前进“”后退“”跳转“这些重定向动作中,浏览器只用最基本的请求头,没有"Cache-Control"所以就会检查缓存,当我们点击刷新的时候,浏览器会在请求头里加”Cache-Control: max-age = 0“因为生存时间是0,而本地的缓存只是保存了几秒,所以不会使用缓存而是向服务器发送请求。
条件请求——验证资源是否失效
浏览器可以用两个连续的请求组成“验证动作”:先是一个HEAD,获取资源的修改时间等元信息,然后与缓存数据比较,如果没有改动就使用缓存,节省网络流量,否则就再发一个GET请求,获取最新的版本。
但这样的两个请求网络成本太高了,所以HTTP就定义了一系列”If”开头的“条件请求”字段,专门用来检查验证资源是否过期,把两个请求的工作合并成一个,验证的责任也交给服务器。
条件请求有5个头字段,最常用的是”if—Modified--Since”和”if—None—Match需要提供第一次响应报文中的”Last--modified”(文件最后的修改时间)和”ETag”(实体标签),然后第二次请求时就可以带上缓存里的原值,验证资源是否是最新的,如果资源没有变,服务器会返回”304 Not Modified”,表示缓存依然有效,浏览器可以更新一下有效期,然后继续使用缓存。
注:ETag是资源的唯一标识,有时候一个文件在一秒内修改了多次,这时候使用”Last--modified”就不太准确,或者文件定期更新,但是内容没有发生改变,只是更换一下说法,这时候用修改时间来判断就会误以为资源改变了,传送给浏览器就会浪费带宽。ETag还有强弱之分,强ETag要求资源在字节级别完全相符,弱ETag只要求资源在语义上没有改变。