客户端浏览器与web服务器进行资源交换是通过HTTP协议为基础的。当客户端接收到服务器端响应的资源后,如果后续再次访问该资源时,就需要进行一些策略的选择,是使用本地已经接收到的资源,还是再次向服务器发送请求重新获得资源。如果使用前者的话,可能此时服务器的资源已经被更新或,显示给用户的信息就是过时的了,如果使用后者的话,已经下载到客户端的资源就白白浪费掉了,并且如果服务器的资源信息没有更新过,则既浪费了服务器的带宽又占用了服务器的处理时间。所以两者往往都是相互结合使用的。使既能提高服务器性能,又能提高客户端的响应速度。客户端向服务器请求资源的大致过程如下:
浏览器先查看本地缓存是否可以使用,如果可以使用则直接使用本地资源,否则向服务器发送资源请求,服务器收到客户端发送的请求后,判断资源的ETag和发送过来的If-None-Match是否满足一定关系,或者是Last-Modified与发送过来的If-Modified-Since是否满足一定关系(具体使用哪种根据服务器而定),如果相等说明资源没有被修改过,则直接向客户端浏览器发送反馈信息,信息头部的Status Code为304,表示让客户端浏览器使用本地的缓存资源。
本地缓存的使用
Expires
服务器在向客户端浏览器发送资源时,可以指定资源的过期时间,在有效期时间内,客户端浏览器就可以一直使用本地缓存而不必向服务器发送请求,从而可以降低服务器的负载并且提高浏览器的响应时间。
服务器端示例代码:
header("Content-Type:image/png"); header("Last-Modified:".gmdate("D d M Y H:i:s")." GMT"); //让图片缓存在客户端浏览器中60秒内有效,超过60秒重新向服务器发起请求 header("Expires:".gmdate("D,d M Y H:i:s",time()+60)." GMT"); $buff = file_get_contents("./images/images.png"); echo $buff;
当发送请求后,查看浏览器网络请求数据为(以浏览器本地时间为准,过期时间比Last-Modified时间晚1分钟)。
在一分钟内请求时的结果为(显示从本地磁盘读取的数据,说明没有向服务器发送请求)
过完一分钟后的请求结果表明重新向服务器端发送了请求,并且设置了新的过期时间。
Cache-Control
Expiries设置的过期时间是以服务器返回的Last-Modified时间为准计算的,当客户端与服务器的时间不一致时,则会影响客户端浏览器本地缓存资源的正常使用,为了防止此种情况,可以设置Cache-Control让缓存的过期时间以客户端的时间为准。当同时设置了Cache-Control和Expiries时,Cache-Control的优先级更高。
询问服务器资源是否更改过
Last-Modified/If-Modified-Since
当服务器第一次向客户端浏览器发送资源时,服务器设置生成该资源的最后修改时间M1,并将该值设置为消息头部的Last-Modified属性,告诉客户端浏览器资源的最后修改时间,客户端后续向服务器发送发送该资源请求时,将Last-Modified的值M2方法消息头部的If-Modified-Since属性中发送给服务器,服务器判断M2和M1是否相等,如果相等,说明资源没有发生改变,向客户端浏览器发送304响应。如果不相等说明M2值发生改变(资源修改过),则将新修改过的资源发送给客户端。 客户端接收响应内容以后,判断如果status code 为304,则直接使用本地缓存的文件。
服务器端代码:
$req_arr = apache_request_headers(); # 模拟实现验证 if(isset($req_arr['If-Modified-Since'])) { $req_since = $req_arr['If-Modified-Since']; if(strtotime($req_since) +60 > time()) { header("http/1.1 304"); exit; } } header("Last-Modified:".gmdate("D d M Y H:i:s")." GMT"); echo date('Y-m-d H:i:s',time())
第一次请求后的结果(服务器返回的状态码为200)
资源有效期内再次访问的结果(服务器返回的状态码为304)
ETag/If-None-Match
这种方式,不使用资源的最后修改时间作判断依据,而是一种资源的标识做比较,称作ETag方式,这种方式和Last-Modified方式比较相似。
服务器端示例代码:
$unix_time = strtotime(date('Y-m-d H:i',time())); $req_arr = apache_request_headers(); if(isset($req_arr['If-None-Match'])) { $req_etag = $req_arr['If-None-Match']; if($req_etag == $unix_time) { header("http/1.1 304"); exit; } } header("ETag:".$unix_time); echo date('Y-m-d H:i:s',time());
参考文献:<构建高性能WEB站点>