🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
# 3.7 Web缓存 ## 3.7 Web缓存 ### 什么是Web缓存 **Web缓存**是指[HTTP协议定义的缓存](https://tools.ietf.org/html/rfc2616#section-13)。它可以有效提高网站的访问速度、降低对网络带宽的消耗。 Web缓存包括网站的反向代理(Reverse Proxy)缓存,中间代理(Intermediary Proxy)缓存以及浏览器缓存,如图所示: ![web-cache](https://box.kancloud.cn/360e590bbfcabc7cff1cbe9e0d9ce22e_682x55.png) 我们在上一节中提到的Web缓存,专指通过网站反向代理提供的Web缓存——但Web缓存的工作原理都是一样的。 ### Web缓存的原理 图中,浏览器发出的HTTP请求在到达源服务器之前要经过一个中间代理和一个反向代理(一共涉及三次HTTP会话),连同浏览器本身在内一共有三个缓存。如果所请求的资源已经存在于任何一个缓存之中并且没有过期,那么这个缓存就可以用于响应对该资源的请求,而不必继续向源服务器转发请求。如果该资源没有被缓存,请求最终到达源服务器,源服务器可以给被请求的资源指定一个过期时间(通过HTTP应答头),这样当HTTP应答原路返回之后,该资源可以被路径上的缓存所缓存起来以备后续使用。另外,当缓存的资源过期后,如果客户端(可能是浏览器、中间代理或者反向代理)再次请求该资源,可以(通过HTTP请求头)附上一些对该资源的校验条件(Validator),服务器如果通过校验,则说明资源没有改变、客户端可以继续使用缓存的资源。 一个具体的例子如下: 程序从浏览器发起一个HTTP请求(比如通过XMLHttpRequest): ``` GET /some/resourse HTTP/1.1 Host: www.example.com ``` 如果资源`/some/resourse`没有被缓存,最终它将到达`www.example.com`的源服务器。后者可以做如下应答: ``` HTTP/1.1 200 OK Date: Sun, 07 Aug 2016 08:50:58 GMT Cache-Control: public, max-age=3600 ETag: 167a264dd3694616870b98d613ae7700 Content-Type: application/json Content-Length: 1234 ... ``` 应答头`Cache-Control: public, max-age=3600`表明:该资源可以做(公共)缓存,有效期3600秒——从`Date`指示的应答产生时间开始计算。这个应答会被浏览器以及浏览器和源服务器之间的代理(Proxy)所缓存下来。如果在3600秒内从浏览器再发起相同的`GET /some/resourse`请求,那么缓存而不是源服务器就可以做出(相同的)应答——在这个例子中浏览器缓存会在第一时间做出应答(而发起HTTP请求的程序甚至感觉不到缓存的存在)。如果超过了3600秒,缓存失效,当程序再发起同样的HTTP请求时,浏览器会发出一个**Conditional GET**,如下: ``` GET /some/resourse HTTP/1.1 Host: www.example.com If-None-Match: 167a264dd3694616870b98d613ae7700 ``` 请求头`If-None-Match`表示:如果`/some/resourse`所代表的资源变化了就给我发一份新的。该请求头的值就是上次应答中的`Etag`应答头的值,它用来校验资源是否改变。因此`If-None-Match`这样的请求头又称作Validator。 如果资源发生了改变,服务器应当返回一个新的资源表述,否则,它应该返回代码304,指示资源没有变化,如下: ``` HTTP/1.1 304 Not Modified Date: Sun, 07 Aug 2016 09:55:57 GMT Cache-Control: public, max-age=3600 ETag: 167a264dd3694616870b98d613ae7700 ``` 这个应答不带有消息实体(Message Body)。浏览器和中间缓存收到这个应答会更新该资源的缓存过期时间,同时用原来缓存的应答(加上更新过的应答头)返回给发起HTTP请求的程序。 除了`Cache-Control`和`ETag`,服务器还可以使用`Expires`来指明缓存过期的时间,用`Last-Modified`指明资源的最新修改时间——这样Conditional GET可以使用`If-Modified-Since`作为Validator。例如: ``` GET /some/resourse HTTP/1.1 Host: www.example.com If-Modified-Since: Fri, 05 Aug 2016 10:00:00 GMT ``` ``` HTTP/1.1 200 OK Date: Sun, 07 Aug 2016 08:50:58 GMT Expires: Sun, 07 Aug 2016 09:50:58 GMT Last-Modified: Fri, 05 Aug 2016 16:00:00 GMT Content-Type: application/json Content-Length: 1234 ... ``` 除了设置过期时间和校验,HTTP还提供了一些机制来控制缓存的请求和应答: - 客户端可以通过指定`Cache-Control: max-age=0`来要求服务器提供一个非缓存的最新版本(一般地,浏览器的“刷新”操作会自动加入这个指令) - 缓存一般只针对GET和HEAD方法,其他方法的应答缺省不会被缓存,但服务器可以通过`Cache-Control: public, ...`指令来override此规则 - 如果应答含有`Authentication`或者`Set-Cookie`应答头,该应答缺省不会被缓存,但服务器仍可以通过`Cache-Control`指令来override此规则,比如`Cache-Control: private, ...`指示该应答可以作为(浏览器的)私有缓存,而不应被某个公开代理所缓存 - 服务器可以通过指定`Cache-Control: public, no-cache=HEADER_NAME_LIST`来指定一个列表,列表里的应答头都不会被缓存,应答的其它部分则会被缓存 - 客户端或者服务器可以通过指定`Cache-Control: no-store`来要求应答不被缓存 `Cache-Control`强大而灵活,关于它的更多介绍请参考HTTP协议文本[14.9 Cache-Control](https://tools.ietf.org/html/rfc2616#section-14.9),在这之前我推荐读者先阅读上面提到的HTTP协议文本[13 Caching in HTTP](https://tools.ietf.org/html/rfc2616#section-13)。Web缓存是HTTP协议的重要组成部分,也是最复杂的部分。如果读者要透彻地理解其原理,HTTP协议文本是首要读物。 ### 如何利用Web缓存来加速网站访问 在你还没有意识到的时候,浏览器已经在主动地利用Web缓存来工作了;另外,Web服务器,如Apache、Nginx,缺省地会为网站的静态文件,如.css、.js、.png等,加上ETag等应答头,这样客户端第二次及以后请求这些文件就可以利用Conditional GET来工作了。但我们还可以主动利用Web缓存把事情做得更好: - 我们可以给网站设置Web缓存,比如通过反向代理,来缓存服务器动态生成的内容 - 我们可以给一些动态资源显示地指定一个过期时间 - 我们应当小心处理Cookie和Authentication,避免在不必要地方使用它们,以使动态内容可以被公开(public)缓存;在最坏的情况下,我们仍然能让带有这些应答头的应答作为私有(private)缓存 善用Web缓存,提升用户体验。