# 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缓存,提升用户体验。
- 前言
- 1 Web概述
- 1.1 什么是Web
- 1.2 超文本和超链接
- 1.3 URL
- 1.4 DNS
- 1.5 HTTP
- 1.5.1 客户端请求
- 1.5.2 服务器应答
- 1.5.3 进一步了解HTTP
- 1.6 HTTPS
- 2 Web浏览器
- 2.1 HTML
- 2.1.1 文档类型声明
- 2.1.2 标签和属性
- 2.1.3 文档结构
- 2.1.4 DOM
- 2.1.5 进一步了解HTML
- 2.2 CSS
- 2.2.1 样式与样式表
- 2.2.2 样式表语法
- 2.2.3 级联样式表
- 2.2.4 进一步了解CSS
- 2.3 JavaScript
- 2.3.1 script标签
- 2.3.2 操纵DOM
- 2.3.3 jQuery
- 2.3.4 进一步了解JavaScript
- 2.4 Ajax
- 2.5 移动设备与响应式Web设计
- 3 Web服务器
- 3.1 方法与资源
- 3.2 状态代码
- 3.3 静态内容与动态内容
- 3.4 编程语言与技术
- 3.4.1 CGI
- 3.4.2 PHP
- 3.4.3 Java
- 3.4.4 Python
- 3.4.5 Ruby
- 3.4.6 Node.js
- 3.5 RESTful Web API
- 3.6 服务器架构
- 3.7 Web缓存
- 3.8 服务器推送
- 4 数据库
- 4.1 关系型数据库
- 4.2 NoSQL数据库
- 5 Web服务器的其他组件
- 5.1 Cron
- 5.2 消息队列
- 5.3 邮件服务器
- 6 开发工具与技术
- 6.1 Git
- 6.1.1 Git基础操作
- 6.1.2 Git基本原理
- 6.1.3 进一步了解Git
- 6.2 敏捷开发