Home | 簡體中文 | 繁體中文 | 雜文 | 知乎專欄 | Github | OSChina 博客 | 雲社區 | 雲棲社區 | Facebook | Linkedin | 視頻教程 | 打賞(Donations) | About
知乎專欄多維度架構 微信號 netkiller-ebook | QQ群:128659835 請註明“讀者”

2.3. 緩存技術

首先要說明,很多緩存技術依賴靜態化。下面展示了緩存可能出現的位置。

用戶user -> 瀏覽器緩存 IE/Firefox Cache -> 逆向代理緩存 Reverse proxy Cache -> WEB伺服器緩存 Apache cache -> 應用程序緩存 php cache -> 資料庫緩存 database cache

當然交換機,網絡適配器,硬碟上也有Cache 但這不是我們要討論的範圍。

緩存存儲方式主要是內存和檔案兩種,後者是存於硬碟中。

網站上使用的緩存主要包括五種:

  1. 瀏覽器 緩存

  2. 逆向代理/CDN緩存

  3. WEB伺服器緩存

  4. 應用程序緩存

  5. 資料庫緩存

將上面的緩存合理地,有選擇性的使用可大大提高網站的訪問能力。

總之,想讓你的網站更快,更多並發,答案是cache,cache 再 cache

2.3.1. 瀏覽器緩存

2.3.1.1. Cache-Control

通過 Cache-Control 設置頁面緩存時間

max-age
max-age 格式寫為:max-age=n,n是以秒為單位, 這個值是告知客戶端GMT + N 後頁面過期,緩存伺服器在s-maxage值為空的時候也會使用這個參數的值。

s-maxage
s-maxage的格式跟max-age一樣,只不過他是給緩存伺服器使用的。

must-revalidate
這個參數用來告知客戶端和緩存伺服器,在GET請求的時候必須與源伺服器驗證實體是否為最新版本。

Cache-Control:max-age=1200,s-maxage=3600
			
Last-Modified
這個參數提供了實體最近一次被修改的時間。這個名字起得不錯,當實體被修改了之後,這個參數也就會被修改.
			

ETag

ETag
ETag是根據內容生成的一段hash字元串,採用信息摘要算法,保證每一個頁面有一個唯一字串。
			

expires

expires 是HTTP 1.0 中定義的,已經不能滿足用戶的需要在 HTTP 1.1 加入了max-age,建議使用 max-age替代expires

指令					含義
public				可以在任何地方緩存
private				只能被瀏覽器緩存
no-cache			不能在任何地方緩存
must-revalidate		緩存必須檢查更新版本
proxy-revalidate	代理緩存必須檢查更新版本
max-age				內容能夠被緩存的時期,以秒錶示
s-maxage			覆蓋共享緩存的max-age設置
			

在Squid, Varnish, Apache, Lighttpd, Nginx 中都可是實現HTTP Cache-Control推送,每次修改都需要重新加載,不太靈活。

ExpiresActive On
ExpiresByType image/gif "access plus 1 month"
ExpiresByType image/png "access plus 1 month"
ExpiresByType image/jpeg "access plus 1 month"
ExpiresByType text/css "access plus 1 month"
ExpiresByType text/javascript "access plus 1 month"
ExpiresByType application/x-javascript "access plus 1 month"
ExpiresByType application/x-shockwave-flash "access plus 1 month"


server.modules = (
...
"mod_expire",
...
)

$HTTP["url"] =~ "^/images/" {
expire.url = ( "" => "access 30 days" )
}
			

我喜歡自己控制TTL時間,且每個頁面單獨設置,可以隨時調整設置。

2.3.1.1.1. 在程序中靈活操作 Cache-Control

在MVC框架中每個控製器下的方法都可以單獨操作Cache

Class blog extend Controller{
	blog(){
		header('Cache-Control: max-age=28800');
	}
	list(){
		header('Cache-Control: max-age=3600');
	}
	details(){
		header('Cache-Control: max-age=160');
	}
}
				

你還可以封裝到Controller中

Class blog extend Controller{
	blog(){
		this->cache('28800');
	}
	list(){
		this->cache('3600');
	}
	details(){
		this->cache('160');
	}
}
				
2.3.1.1.2. 非程序檔案緩存處理

首先做一個Rewrite讓程序接管所有圖片請求

url.rewrite = ( "^/(.+)" => "/index.php/$1" )
				

然後程序通過PATHINFO取出圖片URL

http://images.example.com/your/dir/test.jpg => http://images.example.com/index.php/your/dir/test.jpg
				

程序取出 /your/dir/test.jpg 設置 Content-type 並輸出二進制流

詳細參考

				
<?php
    // Test image.
    $images = '/test/foo.png';

    $headers = apache_request_headers();

    if (isset($headers['If-Modified-Since']) && (strtotime($headers['If-Modified-Since']) == filemtime($images))) {
        header('Last-Modified: '.gmdate('D, d M Y H:i:s', filemtime($images)).' GMT', true, 304);
    } else {
        header('Content-Type: image/png');
        print file_get_contents($fn);
		if (file_exists($images)) {
			header('Last-Modified: '.gmdate('D, d M Y H:i:s', filemtime($images)).' GMT', true, 200);
			header("Cache-Control: max-age=3600, must-revalidate");
			header('Content-Length: '.filesize($images));
			header('Content-type: ' .mime_content_type($images));
			flush();
			readfile($images);
			exit;
		}
    }
				
					

javascript 檔案也可以使用類似方法處理

				
	private function js($file){
		if (file_exists($file)) {
			header("Cache-Control: max-age=3600, must-revalidate");
			header('Content-type: text/javascript');
			flush();
			readfile($file);
			exit;
		}
	}
				
					

2.3.1.2. Expires

只要向瀏覽器輸出過期時間HTTP協議頭,不論是html還是動態腳本,都能被緩存。

HTML META

				
<meta http-equive="Expires" content=" Mon, 10 Jan 2000 00:00:00 GMT"/>
<meta http-equive="Cache-Control" content="max-age=300"/>
<meta http-equive="Cache-Control" content="no-cache"/>
				
				

動態腳本

				
Expires: Mon, 10 Jan 2000 00:00:00 GMT
Cache-Control: max-age=300
Cache-Control: no-cache

header("Expires: " .gmdate ("D, d M Y H:i:s", time() + 3600 * 24 * 7). " GMT");
header("Cache-Control: max-age=300");
header("Cache-Control: no-cache");
				
				

很多web server都提供 Expires 模組

[提示]提示

有些瀏覽器可能不支持。

2.3.1.3. If-Modified-Since / Last-Modified

If-Modified-Since 小於 Last-Modified 返回 200

				
neo@neo-OptiPlex-780:/tmp$ curl -I http://www.163.com/
HTTP/1.1 200 OK
Server: nginx
Content-Type: text/html; charset=GBK
Transfer-Encoding: chunked
Vary: Accept-Encoding
Expires: Mon, 16 May 2011 08:12:05 GMT
Cache-Control: max-age=80
Vary: User-Agent
Vary: Accept
Age: 38
X-Via: 1.1 ls100:8106 (Cdn Cache Server V2.0), 1.1 lydx156:8106 (Cdn Cache Server V2.0)
Connection: keep-alive
Date: Mon, 16 May 2011 08:11:23 GMT
				
				

If-Modified-Since 大於 Last-Modified 返回 304

				
neo@neo-OptiPlex-780:/tmp$ curl -H "If-Modified-Since: Fri, 12 May 2012 18:53:33 GMT" -I http://www.163.com/
HTTP/1.0 304 Not Modified
Content-Type: text/html; charset=GBK
Cache-Control: max-age=80
Age: 41
X-Via: 1.0 ls119:80 (Cdn Cache Server V2.0), 1.0 lydx154:8106 (Cdn Cache Server V2.0)
Connection: keep-alive
Date: Mon, 16 May 2011 08:11:14 GMT
Expires: Mon, 16 May 2011 08:11:14 GMT
				
				

2.3.1.4. ETag / If-None-Match

				
neo@neo-OptiPlex-780:/tmp$ curl -I http://images.example.com/test/test.html
HTTP/1.1 200 OK
Cache-Control: s-maxage=7200, max-age=900
Expires: Mon, 16 May 2011 09:48:45 GMT
Content-Type: text/html
Accept-Ranges: bytes
ETag: "1984705864"
Last-Modified: Mon, 16 May 2011 09:01:07 GMT
Content-Length: 22
Date: Mon, 16 May 2011 09:33:45 GMT
Server: lighttpd/1.4.26
				
				
				
neo@neo-OptiPlex-780:/tmp$ curl -H 'If-None-Match: "1984705864"' -I http://images.example.com/test/test.html
HTTP/1.1 304 Not Modified
Cache-Control: s-maxage=7200, max-age=900
Expires: Mon, 16 May 2011 09:48:32 GMT
Content-Type: text/html
Accept-Ranges: bytes
ETag: "1984705864"
Last-Modified: Mon, 16 May 2011 09:01:07 GMT
Date: Mon, 16 May 2011 09:33:32 GMT
Server: lighttpd/1.4.26
				
				

2.3.2. CDN (Content Delivery Network) 與反向代理緩存

具有代表性的逆向代理伺服器:

  1. Squid

  2. Nginx

  3. Varnish

  4. Apache cache module

其它逆向代理伺服器

  1. 一些提供cache的硬件設備

  2. 最近幾年出現了的 China Cache 服務商,也稱CDN

很多CDN廠商使用Squid 二次開發做為CDN節點,通過全球負載均衡使用分發

這些CDN廠商主要做了一下二次開發

  1. logs 日誌集中

  2. 流量限制

  3. push,pull操作

  4. url 刷新

s-maxage 與 max-age用法類似,s-maxage針對代理伺服器緩存。同樣適用於CDN

s-maxage 與 max-age 組合使用可以提高CDN性能

2.3.2.1. CDN介面API

與CDN有關的開發工作

CDN 內容更新,一般廠商會提供一個SOAP介面,你可以通過介面刷新你的內容。但介面有限制,不能隨意使用,一般是多少秒可以刷新一次,或者一天可以刷新幾次

2.3.2.2. 方向代理頁面過期處理

方向代理一般都支持PURGE協議,Squid,Varnish等等向管理連接埠發送 PURGE 即可是使用頁面刷新

PURGE http://netkiller.github.net/index.html
				

有些方向代理如:Varnish 可以使用正則表達式

同時這些代理伺服器都承受管理命令

squid: squidclient

varnish: varnishadm

2.3.2.3. 內容版本化

例如這樣的URL

http://images.example.com/logo.gif
http://images.example.com/product.jpg
				

我們可以通過Rewrite或PATHINFO等技術做為靜態化。例如首次版本

http://images.example.com/logo.1.gif		=> logo.gif
http://images.example.com/product.1.jpg		=> product.jpg
				

原圖發生變化後,版本遞增

http://images.example.com/logo.2.gif		=> logo.gif
http://images.example.com/product.2.jpg		=> product.jpg
				

就的URL將丟棄

http://images.example.com/logo.1.gif
http://images.example.com/product.1.jpg
				

CDN 就回源去下面的URL,並且取到的是新圖

http://images.example.com/logo.2.gif
http://images.example.com/product.2.jpg
				

2.3.3. 負載均衡設備

F5 Big-IP, Array 等設備都提供硬件加速,其原理與squid, apache提供的功能大同小異

其中Array 頁面壓縮採用硬件壓縮卡實現,SSL加速也採用硬件實現

2.3.4. WEB伺服器緩存

例如,通過配置apache實現自身 cache

2.3.5. 應用程序緩存

在這個領域百花齊放,相信你一定能找到適合你的。這些cache會為你提供一些api,來訪問它。

代表性的 memcached 據我所是sina廣泛使用,騰訊也曾經使用過後來開發了TC(Tencent Cache),台灣雅虎則使用APC Cache。

另外模板引擎也有自己的緩存系統

2.3.6. 資料庫緩存

資料庫本身就有這個配置選項,如果需要你仍然可以在資料庫前面加一道Cache。

例如PostgreSQL, MySQL 都提供參數可以將memcached編譯到它內部