# 3.7 Web緩存
## 3.7 Web緩存
### 什么是Web緩存
**Web緩存**是指[HTTP協議定義的緩存](https://tools.ietf.org/html/rfc2616#section-13)。它可以有效提高網站的訪問速度、降低對網絡帶寬的消耗。
Web緩存包括網站的反向代理(Reverse Proxy)緩存,中間代理(Intermediary Proxy)緩存以及瀏覽器緩存,如圖所示:

我們在上一節中提到的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 敏捷開發