實操 Web Cache (第二版)

實操 Web Cache

http://netkiller.github.io/journal/cache.html

Mr. Neo Chen (陳景峯), netkiller, BG7NYT


中國廣東省深圳市龍華新區民治街道溪山美地
518131
+86 13113668890
+86 755 29812080
<netkiller@msn.com>javascript

$Idphp

版權聲明 css

轉載請與做者聯繫,轉載時請務必標明文章原始出處和做者信息及本聲明。html

       
文檔出處:
http://netkiller.github.io
http://netkiller.sourceforge.net

2015-08-27java

摘要python

寫這篇文章的緣由,是我看到網上不少談這類的文章,可能是人云亦云,不求實事,誤導讀者。mysql

下面文中我會一個一個作實驗,並展現給你,說明爲何會這樣。只有本身親自嘗試才能拿出有說服力的真憑實據。linux

2014-03-12 首次發佈nginx

2015-08-27 修改,增長特殊數據緩存git

個人系列文檔

Netkiller Architect 手札 Netkiller Developer 手札 Netkiller PHP 手札 Netkiller Python 手札 Netkiller Testing 手札
Netkiller Cryptography 手札 Netkiller Linux 手札 Netkiller Debian 手札 Netkiller CentOS 手札 Netkiller FreeBSD 手札
Netkiller Shell 手札 Netkiller Security 手札 Netkiller Web 手札 Netkiller Monitoring 手札 Netkiller Storage 手札
Netkiller Mail 手札 Netkiller Docbook 手札 Netkiller Project 手札 Netkiller Database 手札 Netkiller PostgreSQL 手札
Netkiller MySQL 手札 Netkiller NoSQL 手札 Netkiller LDAP 手札 Netkiller Network 手札 Netkiller Cisco IOS 手札
Netkiller H3C 手札 Netkiller Multimedia 手札 Netkiller Perl 手札 Netkiller Amateur Radio 手札 Netkiller DevOps 手札

目錄

1. 測試環境

CentOS 6.5

Nginx安裝腳本 https://github.com/oscm/shell/blob/master/nginx/nginx.sh

php安裝腳本 https://github.com/oscm/shell/blob/master/php/5.5.8.sh

2. 文件修改日期 If-Modified-Since / Last-Modified

If-Modified-Since 小於 Last-Modified 返回 HTTP/1.1 200 OK, 不然返回 HTTP/1.0 304 Not Modified

每次瀏覽器請求文件會攜帶 If-Modified-Since 頭,將當前時間發送給服務器,與服務器的Last-Modified時間對對比,若是大於Last-Modified時間,返回HTTP/1.0 304 Not Modified不會從新打開文件,不然從新讀取文件並返回內容

2.1. 靜態文件

nginx/1.0.15 靜態文件自動產生 Last-Modified 頭

# nginx -v
nginx version: nginx/1.0.15

# curl -I http://192.168.6.9/index.html
HTTP/1.1 200 OK
Server: nginx/1.0.15
Date: Thu, 27 Feb 2014 07:36:03 GMT
Content-Type: text/html
Content-Length: 6
Last-Modified: Thu, 27 Feb 2014 07:29:50 GMT
Connection: keep-alive
Accept-Ranges: bytes

圖片文件

# curl -I http://192.168.6.9/image.png
HTTP/1.1 200 OK
Server: nginx/1.0.15
Date: Thu, 27 Feb 2014 07:37:18 GMT
Content-Type: image/png
Content-Length: 41516
Last-Modified: Thu, 27 Feb 2014 07:36:59 GMT
Connection: keep-alive
Accept-Ranges: bytes

提示

疑問 nginx/1.4.5 默認沒有 Last-Modified

# nginx -v
nginx version: nginx/1.4.5

# curl -I http://192.168.2.15/index.html
HTTP/1.1 200 OK
Server: nginx/1.4.5
Date: Fri, 28 Feb 2014 02:13:44 GMT
Content-Type: text/html
Connection: keep-alive

通過一番周折最終找到答案 Nginx 若是開啓 ssi 會禁用Last-Modified 關閉 ssi 後輸出以下

# curl -I  http://localhost/index.html
HTTP/1.1 200 OK
Server: nginx/1.4.5
Date: Fri, 28 Feb 2014 05:44:29 GMT
Content-Type: text/html
Content-Length: 6
Last-Modified: Wed, 25 Dec 2013 03:18:16 GMT
Connection: keep-alive
ETag: "52ba4e78-6"
Accept-Ranges: bytes

再測試一次

# curl -H "If-Modified-Since: Fir, 28 Feb 2014 07:42:55 GMT" -I http://192.168.2.15/test.html
HTTP/1.1 304 Not Modified
Server: nginx/1.4.5
Date: Fri, 28 Feb 2014 02:34:54 GMT
Last-Modified: Fri, 28 Feb 2014 01:55:50 GMT
Connection: keep-alive
ETag: "530feca6-8b"

測試結果成功返回 HTTP/1.1 304 Not Modified, 但又莫名其妙的出現了 ETag。 這就是Nignx本版差別,很是混亂。

既然出現了ETag咱們也順便測試一下

# curl -H 'If-None-Match: "530feca6-8b"' -I http://192.168.2.15/test.html
HTTP/1.1 304 Not Modified
Server: nginx/1.4.5
Date: Fri, 28 Feb 2014 02:39:18 GMT
Last-Modified: Fri, 28 Feb 2014 01:55:50 GMT
Connection: keep-alive
ETag: "530feca6-8b"

也是成功的

測試圖片

# curl -I http://localhost/logo.jpg
HTTP/1.1 200 OK
Server: nginx/1.4.5
Date: Fri, 28 Feb 2014 02:59:04 GMT
Content-Type: image/jpeg
Content-Length: 10103
Last-Modified: Fri, 28 Feb 2014 02:56:37 GMT
Connection: keep-alive
ETag: "530ffae5-2777"
Accept-Ranges: bytes


# curl -H 'If-None-Match: "530ffae5-2777"' -I http://localhost/logo.jpg
HTTP/1.1 304 Not Modified
Server: nginx/1.4.5
Date: Fri, 28 Feb 2014 03:03:33 GMT
Last-Modified: Fri, 28 Feb 2014 02:56:37 GMT
Connection: keep-alive
ETag: "530ffae5-2777"

# curl -H "If-Modified-Since: Fri, 28 Feb 2014 12:04:18 GMT" -I http://localhost/logo.jpg
HTTP/1.1 200 OK
Server: nginx/1.4.5
Date: Fri, 28 Feb 2014 03:04:45 GMT
Content-Type: image/jpeg
Content-Length: 10103
Last-Modified: Fri, 28 Feb 2014 02:56:37 GMT
Connection: keep-alive
ETag: "530ffae5-2777"
Accept-Ranges: bytes

測試結果,ETag經過測試,If-Modified-Since不管如何也沒法返回 304 可能還須要其餘的HTTP頭,瀏覽器測試都經過返回 HTTP/1.1 304 Not Modified

如今換成瀏覽器測試 Chrome Firefox成功, 由於瀏覽器不會主動發送If-Modified-Since, 瀏覽器只有發現Last-Modified後,第二次請求才會推送 If-Modified-Since 須要刷新兩次頁面。

2.1.1. if_modified_since

在開啓ssi的狀況下,經過參數 if_modified_since 能夠開啓 Last-Modified

server {
    listen       80;
    server_name  192.168.2.15;
    if_modified_since before;
}

測試結果看不到 Last-Modified, 由於 Nginx 的 if_modified_since before;參數只有接收到瀏覽器發過來的If-Modified-Since頭纔會發送Last-Modified

# curl -I http://192.168.2.15/test.html
HTTP/1.1 200 OK
Server: nginx/1.4.5
Date: Fri, 28 Feb 2014 02:39:42 GMT
Content-Type: text/html
Connection: keep-alive

最終 if_modified_since before; 數沒有起到做用

參數設置爲 if_modified_since exact;

# curl -I http://192.168.2.15/test.html
HTTP/1.1 200 OK
Server: nginx/1.4.5
Date: Fri, 28 Feb 2014 02:45:40 GMT
Content-Type: text/html
Connection: keep-alive

# curl -H 'If-None-Match: "530feca6-8b"' -I http://192.168.2.15/test.html
HTTP/1.1 304 Not Modified
Server: nginx/1.4.5
Date: Fri, 28 Feb 2014 02:45:44 GMT
Last-Modified: Fri, 28 Feb 2014 01:55:50 GMT
Connection: keep-alive
ETag: "530feca6-8b"

# curl -H "If-Modified-Since: Fir, 28 Feb 2014 07:42:55 GMT" -I http://192.168.2.15/test.html
HTTP/1.1 200 OK
Server: nginx/1.4.5
Date: Fri, 28 Feb 2014 02:45:50 GMT
Content-Type: text/html
Connection: keep-alive

測試失敗,瀏覽器也是實測失敗,ETag卻成功

2.2. 經過rewrite僞靜態處理

index.php仍然是上面的那個php文件,咱們只是作了僞靜態

location / {
        root   /www;
        index  index.html index.htm;
		rewrite ^/test.html$ /index.php last;
}

如今咱們分別經過curl有chrome/firefox進行測試

# curl -H "If-Modified-Since: Fri, 28 Feb 2014 08:42:55 GMT" -I  http://192.168.6.9/test.html
HTTP/1.1 200 OK
Server: nginx/1.0.15
Date: Thu, 27 Feb 2014 08:55:19 GMT
Content-Type: text/html
Connection: keep-alive
Last-Modified: Thu, 26 Feb 2014 08:39:35 GMT

通過測試不管是 curl 仍是 chrome/firefox 均沒法返回304.

下面是個人分析,僅供參考。用戶請求index.html Nginx 會找到該文件讀取 mtime 與 If-Modified-Since 匹配,若是If-Modified-Since大於 Last-Modified返回 304不然返回200.

爲何一樣操做通過僞靜態的test.html就不行呢? 我分析當用戶請求test.html Nginx 首先作Rewrite處理,而後跳轉到index.php 整個過程nginx 並無訪問實際物理文件test.html也就沒有mtime, 因此Nginx 返回200.

若是 Nginx 按預想的返回304,nginx 須要讀取程序返回的HTTP頭,Nginx 並無這樣的處理邏輯。

2.3. 動態文件

動態文件沒有 Last-Modified 頭,咱們能夠僞造一個

# curl -I http://192.168.6.9/index.php
HTTP/1.1 200 OK
Server: nginx/1.0.15
Date: Thu, 27 Feb 2014 07:57:59 GMT
Content-Type: text/html
Connection: keep-alive

在程序中加入HTTP頭推送操做,Last-Modified時間是27號,當前時間是28號,咱們要讓Last-Modified 小於當前時間才行。

# cat index.php
<?php
header('Last-Modified: Thu, 27 Feb 2014 08:39:35 GMT' );
//header('Last-Modified: ' .gmdate('D, d M Y H:i:s') . ' GMT' );
?>
Hello

如今你將看到 Last-Modified

# curl -I http://localhost/modified.php
HTTP/1.1 200 OK
Server: nginx/1.4.5
Date: Fri, 28 Feb 2014 05:59:28 GMT
Content-Type: text/html
Connection: keep-alive
Last-Modified: Fri, 28 Feb 2014 10:04:18 GMT

注意

雖然咱們讓動態程序返回了 Last-Modified ,但瀏覽器不認,通過測試 Chrome / Firefox 均不會認可.php文件,並緩存其內容。

# curl -I http://localhost/modified.php
HTTP/1.1 200 OK
Server: nginx/1.4.5
Date: Fri, 28 Feb 2014 05:59:28 GMT
Content-Type: text/html
Connection: keep-alive
Last-Modified: Fri, 28 Feb 2014 10:04:18 GMT

# curl -H "If-Modified-Since: Fri, 28 Feb 2014 08:42:55 GMT" -I  http://localhost/modified.php
HTTP/1.1 200 OK
Server: nginx/1.0.15
Date: Fri, 28 Feb 2014 05:32:30 GMT
Content-Type: text/html
Connection: keep-alive
Last-Modified: Thu, 26 Feb 2014 08:39:35 GMT

Last-Modified 對動態程序來講沒有起到實際做用

Last-Modified是程序產生的,Nginx沒法讀到,讓程序去處理狀態返回是可行的,下面咱們修改程序以下。

# cat modified.php
<?php
$mtime = 'Fri, 28 Feb 2014 12:04:18 GMT';
cache($mtime);
function cache($mtime)
{
	$http_if_modified_since = null;
	if(array_key_exists ('HTTP_IF_MODIFIED_SINCE',$_SERVER)){
		$http_if_modified_since = $_SERVER['HTTP_IF_MODIFIED_SINCE'];
	}
	echo $http_if_modified_since;
	if ($http_if_modified_since >= $mtime)
	{
		header('Last-Modified: '.$mtime, true, 304);
		exit;
	} else {
		header('Last-Modified: ' . $mtime );
	}

}
print_r($_SERVER);
echo date("Y-m-d H:i:s");
?>

測試效果

# curl -I http://localhost/modified.php
HTTP/1.1 200 OK
Server: nginx/1.4.5
Date: Fri, 28 Feb 2014 05:22:28 GMT
Content-Type: text/html
Connection: keep-alive

僞造一個 If-Modified-Since 日期小於咱們指定的日期程序返回HTTP/1.1 200 OK

# curl -H "If-Modified-Since: Fri, 28 Feb 2014 10:04:18 GMT" -I http://localhost/modified.php
HTTP/1.1 200 OK
Server: nginx/1.4.5
Date: Fri, 28 Feb 2014 05:22:13 GMT
Content-Type: text/html
Connection: keep-alive

僞造一個 If-Modified-Since 日期大於咱們指定的日期程序返回HTTP/1.1 304 Not Modified

# curl -H "If-Modified-Since: Fri, 28 Feb 2014 20:04:18 GMT" -I http://localhost/modified.php
HTTP/1.1 304 Not Modified
Server: nginx/1.4.5
Date: Fri, 28 Feb 2014 05:21:31 GMT
Connection: keep-alive
Last-Modified: Fri, 28 Feb 2014 12:04:18 GMT

測試成功,而且在瀏覽器端也測試成功 HTTP/1.1 304 Not Modified

將modified.php僞靜態處理

location / {
        root   /www;
        index  index.html index.htm;
		rewrite ^/modified.html$ /modified.php last;
    }

測試

# curl -I http://localhost/modified.html
HTTP/1.1 200 OK
Server: nginx/1.4.5
Date: Fri, 28 Feb 2014 06:21:10 GMT
Content-Type: text/html
Connection: keep-alive
Last-Modified: Fri, 28 Feb 2014 10:04:18 GMT

# curl -H "If-Modified-Since: Fri, 28 Feb 2014 12:04:18 GMT" -I http://localhost/modified.html
HTTP/1.1 304 Not Modified
Server: nginx/1.4.5
Date: Fri, 28 Feb 2014 06:21:22 GMT
Connection: keep-alive
Last-Modified: Fri, 28 Feb 2014 10:04:18 GMT

達到預期效果

3. ETag / If-None-Match

上面的Last-Modified測試中發現ETag雖然不限制,可是暗中仍是可用的:)

etag on; 開啓Nginx etag支持,lighttpd 默認開啓

server {
    listen       80;
    server_name phalcon;

    charset utf-8;

    access_log  /var/log/nginx/host.access.log  main;
	etag on;
    location / {
        root   /www/phalcon/public;
        index  index.html index.php;
    }
}

檢查ETag輸出

# curl -I http://localhost/index.html
HTTP/1.1 200 OK
Server: nginx/1.4.5
Date: Fri, 28 Feb 2014 03:08:28 GMT
Content-Type: text/html
Connection: keep-alive

# curl -I http://phalcon/img/css.png
HTTP/1.1 200 OK
Server: nginx
Date: Thu, 27 Feb 2014 09:20:49 GMT
Content-Type: image/png
Content-Length: 1133
Last-Modified: Fri, 14 Feb 2014 08:05:03 GMT
Connection: keep-alive
ETag: "52fdce2f-46d"
Accept-Ranges: bytes3

即便你開啓了 ETag Nginx 對 HTML、CSS文件也不作處理。最終在一個外國網站是找到一個nginx-static-etags模塊,有興趣本身嘗試,這裏就不講了。

3.1. 靜態文件

首先查詢etag值

# curl -I http://phalcon/img/css.png
HTTP/1.1 200 OK
Server: nginx
Date: Thu, 27 Feb 2014 09:25:41 GMT
Content-Type: image/png
Content-Length: 1133
Last-Modified: Fri, 14 Feb 2014 08:05:03 GMT
Connection: keep-alive
ETag: "52fdce2f-46d"
Accept-Ranges: bytes

而後向服務器發送If-None-Match HTTP頭

# curl -H 'If-None-Match: "52fdce2f-46d"' -I http://phalcon/img/css.png
HTTP/1.1 304 Not Modified
Server: nginx
Date: Thu, 27 Feb 2014 09:25:44 GMT
Last-Modified: Fri, 14 Feb 2014 08:05:03 GMT
Connection: keep-alive
ETag: "52fdce2f-46d"

此次比較順利,成功返回HTTP/1.1 304 Not Modified

3.2. 動態程序

默認狀況輸出以下

# curl -I http://192.168.6.9/index.php
HTTP/1.1 200 OK
Server: nginx
Date: Thu, 27 Feb 2014 09:29:13 GMT
Content-Type: text/html; charset=utf-8
Connection: keep-alive

測試程序

<?php
header('Last-Modified: Thu, 26 Feb 2014 08:39:35 GMT' );
header('Etag: "abcdefg"');
#header('Last-Modified: ' .gmdate('D, d M Y H:i:s') . ' GMT' );
?>
Hello

測試效果

# curl -I http://192.168.6.9/index.php
HTTP/1.1 200 OK
Server: nginx/1.0.15
Date: Thu, 27 Feb 2014 09:41:06 GMT
Content-Type: text/html
Connection: keep-alive
Last-Modified: Thu, 26 Feb 2014 08:39:35 GMT
Etag: "abcdefg"

[root@centos6 ~]# curl -H 'If-None-Match: "abcdefg"' -I http://192.168.6.9/index.php
HTTP/1.1 200 OK
Server: nginx/1.0.15
Date: Thu, 27 Feb 2014 09:41:42 GMT
Content-Type: text/html
Connection: keep-alive
Last-Modified: Thu, 26 Feb 2014 08:39:35 GMT
Etag: "abcdefg"

測試狀況與以前的Last-Modified結果同樣

動態程序返回Etag真的就沒有用了嗎?

答案是:非也, 有一個方法可讓動態程序返回的 Etag 也能發揮做用,程序修改以下

<?php
$etag = md5('http://netkiller.github.io');
cache($etag);
function cache($etag)
{
        $http_if_none_match = null;
        if(array_key_exists ('HTTP_IF_NONE_MATCH',$_SERVER)){
                $http_if_none_match = $_SERVER['HTTP_IF_NONE_MATCH'];
        }

        if ($http_if_none_match == $etag)
        {
                header('Etag: '.$etag, true, 304);
                exit;
        } else {
                header('Etag: '.$etag);
        }

}
print_r($_SERVER);
echo date("Y-m-d H:i:s");
?>

首先查看Etag值

# curl  -I http://192.168.6.9/test.php
HTTP/1.1 200 OK
Server: nginx/1.0.15
Date: Thu, 27 Feb 2014 10:07:19 GMT
Content-Type: text/html
Connection: keep-alive
Etag: 7467675324d0f7a3e01ce5151848fedb

發送If-None-Match頭

# curl -H 'If-None-Match: 7467675324d0f7a3e01ce5151848fedb' -I http://192.168.6.9/test.php
HTTP/1.1 304 Not Modified
Server: nginx/1.0.15
Date: Thu, 27 Feb 2014 10:07:39 GMT
Connection: keep-alive
Etag: 7467675324d0f7a3e01ce5151848fedb

達成預計效果,此種方法一樣能夠用於 Last-Modified,僞靜態後效果更好

Etag 值的運算技巧,我習慣上採用URL同時配合僞靜態例如

$etag = $_SERVER['REQUEST_URI']

URL相似 http://www.example.com/news/100/1000.html 一次請求便緩存頁面,這樣帶來一個更新的問題,因而又作了這樣的處理

http://www.example.com/news/100/1000.1.html

.1.是版本號,每次修改後+1操做,.1.沒有人格意義rewrite操做是會丟棄這個參數,僅僅是爲了始終有新的URL對應內容

4. Expires / Cache-Control

前面所講 Last-Modified 與 Etag 主要用於分辨文件是否修改過, 沒法控制頁面在瀏覽器端緩存的時間。Expires / Cache-Control 能夠控制緩存的時間段

Expires 是 HTTP/1.0標準,Cache-Control是 HTTP/1.1標準。都能正常工做,HTTP/1.1規範中max-age優先級高於Expires,有些瀏覽器會聯動設置,例如你設置了Cache-Control隨之自動生成Expires,僅僅爲了兼容。

4.1. 靜態文件

首先配置nginx設置html與png文件緩存1天

location ~ .*\.(html|png)$
{
    expires      1d;
}

當前狀況

# curl -I http://192.168.6.9/index.html
HTTP/1.1 200 OK
Server: nginx/1.0.15
Date: Thu, 27 Feb 2014 10:47:08 GMT
Content-Type: text/html
Content-Length: 6
Last-Modified: Thu, 27 Feb 2014 07:29:50 GMT
Connection: keep-alive
Accept-Ranges: bytes

重啓Nginx後的HTTP協議頭多出Expires與Cache-Control

# curl -I http://192.168.6.9/index.html
HTTP/1.1 200 OK
Server: nginx/1.0.15
Date: Thu, 27 Feb 2014 10:42:09 GMT
Content-Type: text/html
Content-Length: 3698
Last-Modified: Fri, 26 Apr 2013 20:36:51 GMT
Connection: keep-alive
Expires: Fri, 28 Feb 2014 10:42:09 GMT
Cache-Control: max-age=86400
Accept-Ranges: bytes

4.2. 動態文件

默認返回

# curl -I http://192.168.6.9/index.php
HTTP/1.1 200 OK
Server: nginx/1.0.15
Date: Thu, 27 Feb 2014 11:45:05 GMT
Content-Type: text/html
Connection: keep-alive

index.php 增長 Cache-Control 輸出控制

header('Cache-Control: max-age=259200');

再次查看

# curl -I http://192.168.6.9/index.php
HTTP/1.1 200 OK
Server: nginx/1.0.15
Date: Thu, 27 Feb 2014 11:53:48 GMT
Content-Type: text/html
Connection: keep-alive
Cache-Control: max-age=259200

如今使用 Chrome 、Firefox 測試,你會發現始終返回200,而且max-age=259200數值不會改變。

緣由是Cache-Control程序輸出的,Nginx並不知道,因此Nginx 不會給你返回304

header('Last-Modified: ' .gmdate('D, d M Y H:i:s') . ' GMT' );

$offset = 60 * 60 * 24;
header('Expires: ' . gmdate('D, d M Y H:i:s', time() + $offset) . ' GMT');

$ttl=3600;
header("Cache-Control: max-age=$ttl, must-revalidate");

這種方法不能實現緩存的目的

5. FastCGI 緩存相關

咱們作個嘗試將 expires 1d;加到location ~ \.php$中,看看能不能實現緩存的目的。

location ~ \.php$ {
        root           /www;
        fastcgi_pass   127.0.0.1:9000;
        fastcgi_index  index.php;
        fastcgi_param  SCRIPT_FILENAME  /www$fastcgi_script_name;
        include        fastcgi_params;
		expires      1d;
    }

測試程序

# cat expires.php
<?php
echo date("Y-m-d H:i:s");
?>

測試結果

# curl -I http://localhost/expires.php
HTTP/1.1 200 OK
Server: nginx/1.4.5
Date: Fri, 28 Feb 2014 04:39:57 GMT
Content-Type: text/html
Connection: keep-alive
Expires: Sat, 01 Mar 2014 04:39:57 GMT
Cache-Control: max-age=86400

雖然推送 Cache-Control: max-age=86400 可是 IE Chrome Firefox 仍不能緩存頁面

6. HTML META 與 Cache

建立一個測試文件以下

<html>
<head>
	<title>Hello</title>
	<meta http-equiv="Cache-Control" content="max-age=7200" />
	<meta http-equiv="expires" content="Fri, 28 Feb 2014 12:04:18 GMT" />
</head>
<body>
	Helloworld
</body>
</html>

測試HTML頁面

# curl -i http://localhost/test.html
HTTP/1.1 200 OK
Server: nginx/1.4.5
Date: Fri, 28 Feb 2014 03:30:45 GMT
Content-Type: text/html
Transfer-Encoding: chunked
Connection: keep-alive

<html>
<head>
	<title>Hello</title>
	<meta http-equiv="Cache-Control" content="max-age=7200" />
	<meta http-equiv="expires" content="Fri, 28 Feb 2014 12:04:18 GMT" />
</head>
<body>
	Helloworld
</body>
</html>

咱們能夠看到HTML頁面中meta設置緩存對Nginx並不起做用, 不少人會說對瀏覽器起做用!

此次我測試了 IE11, Chrome, Firefox 發現都沒法緩存頁面,可能對IE5什麼的還有用,我沒有環境測試,由於10年前咱們在B/S開發常常這樣使用

<meta http-equiv="cache-control" content="max-age=0" />
<meta http-equiv="cache-control" content="no-cache" />
<meta http-equiv="expires" content="0" />
<meta http-equiv="expires" content="Tue, 01 Jan 1980 1:00:00 GMT" />
<meta http-equiv="pragma" content="no-cache" />

至少在當年IE是認這些Meta的,進入HTML5時代不少都發生了變化,因此不能一律而論

7. gzip

defalte 是 Apache httpd 的標準這裏只談gzip

首先建立一個 gzip.html

# curl -I http://localhost/gzip.html
HTTP/1.1 200 OK
Server: nginx/1.4.5
Date: Mon, 03 Mar 2014 01:49:45 GMT
Content-Type: text/html
Content-Length: 19644
Last-Modified: Mon, 03 Mar 2014 01:49:02 GMT
Connection: keep-alive
ETag: "5313df8e-4cbc"
Accept-Ranges: bytes

開啓 gzip on;

server {
    listen       80;
    server_name  localhost;

    #charset utf-8;
    #access_log  /var/log/nginx/log/host.access.log  main;
    #etag on;
    #ssi on;
    gzip on;

如今看看效果

# curl -I http://localhost/gzip.html
HTTP/1.1 200 OK
Server: nginx/1.4.5
Date: Mon, 03 Mar 2014 01:51:56 GMT
Content-Type: text/html
Content-Length: 19644
Last-Modified: Mon, 03 Mar 2014 01:49:02 GMT
Connection: keep-alive
ETag: "5313df8e-4cbc"
Accept-Ranges: bytes

並無什麼不一樣,如今增長HTTP頭Accept-Encoding:gzip,defalte看看

# curl -H Accept-Encoding:gzip,defalte  http://localhost/gzip.html

若是你能看到非文本內容(俗稱亂碼)就表示成功了。輸入內容就是gzip壓縮後二進制數據,咱們使用gunzip能夠解壓縮

# curl -H Accept-Encoding:gzip,defalte  http://localhost/gzip.html | gunzip

若是能正常看到html輸出,表示壓縮無誤。

7.1. gzip 總結

gzip on; 開啓後默認支持 text/html 不能在 gzip_types 再次定義,不然會提示重複MIME類型

Starting nginx: nginx: [warn] duplicate MIME type "text/html" in /etc/nginx/conf.d/localhost.conf:16

高級配置參考

gzip  on;
    gzip_http_version 1.0;
    gzip_types        text/plain text/xml text/css application/xml application/xhtml+xml application/rss+xml application/atom_xml application/javascript application/x-javascript application/json;
    gzip_disable      "MSIE [1-6]\.";
    gzip_disable      "Mozilla/4";
    gzip_comp_level   6;
    gzip_proxied      any;
    gzip_vary         on;
    gzip_buffers      4 8k;
    gzip_min_length   1000;

8. 反向代理與緩存

反向代理服務器緩存方式分爲:

強制緩存,指定文件,擴展名,URL設置緩存時間

遵循HTTP協議頭標準進行緩存

默認配置,只進行代理,不進行緩存

server {
    listen       80;
    server_name  192.168.2.15;
    #access_log  /var/log/nginx/log/host.access.log  main;

	location / {
	  proxy_pass        http://localhost:80;
	  proxy_set_header  X-Real-IP  $remote_addr;
	}
}

反向代理會產生兩條日誌(access_log 寫入一個文件中,若是分開寫,則會分開寫入日誌)

192.168.2.15 - - [28/Feb/2014:18:09:33 +0800] "HEAD /modified.html HTTP/1.1" 200 0 "-" "curl/7.19.7 (x86_64-redhat-linux-gnu) libcurl/7.19.7 NSS/3.14.0.0 zlib/1.2.3 libidn/1.18 libssh2/1.4.2" "-"
127.0.0.1 - - [28/Feb/2014:18:09:33 +0800] "HEAD /modified.html HTTP/1.0" 200 0 "-" "curl/7.19.7 (x86_64-redhat-linux-gnu) libcurl/7.19.7 NSS/3.14.0.0 zlib/1.2.3 libidn/1.18 libssh2/1.4.2" "-"

Last-Modified 與 ETag 會透傳過去

# curl -H "If-Modified-Since: Fri, 28 Feb 2014 12:04:18 GMT" -I http://192.168.2.15/modified.html
HTTP/1.1 304 Not Modified
Server: nginx/1.4.5
Date: Fri, 28 Feb 2014 10:17:30 GMT
Connection: keep-alive
Last-Modified: Fri, 28 Feb 2014 10:04:18 GMT

咱們能夠看到兩條日誌都返回304

192.168.2.15 - - [28/Feb/2014:18:17:30 +0800] "HEAD /modified.html HTTP/1.1" 304 0 "-" "curl/7.19.7 (x86_64-redhat-linux-gnu) libcurl/7.19.7 NSS/3.14.0.0 zlib/1.2.3 libidn/1.18 libssh2/1.4.2" "-"
127.0.0.1 - - [28/Feb/2014:18:17:30 +0800] "HEAD /modified.html HTTP/1.0" 304 0 "-" "curl/7.19.7 (x86_64-redhat-linux-gnu) libcurl/7.19.7 NSS/3.14.0.0 zlib/1.2.3 libidn/1.18 libssh2/1.4.2" "-"

下面爲反向代理增長緩存功能

proxy_temp_path   /tmp/proxy_temp_dir;
proxy_cache_path  /tmp/proxy_cache_dir  levels=1:2   keys_zone=nginx_cache:200m inactive=3d max_size=30g;

server {
    listen       80;
    server_name  192.168.2.15;

	location / {
		proxy_cache nginx_cache;
		proxy_cache_key $host$uri$is_args$args;
		proxy_set_header  X-Real-IP  $remote_addr;
		proxy_set_header  X-Forwarded-For  $proxy_add_x_forwarded_for;
		proxy_cache_valid 200 10m;
		proxy_pass        http://localhost;
	}

	location ~ .*\.(php|jsp|cgi)?$
	{
	     proxy_set_header Host  $host;
	     proxy_set_header X-Forwarded-For  $remote_addr;
	     proxy_pass http://backend_server;
	}
}

# curl  -I http://192.168.2.15/index.html
HTTP/1.1 200 OK
Server: nginx/1.4.5
Date: Fri, 28 Feb 2014 10:57:35 GMT
Content-Type: text/html
Content-Length: 12
Connection: keep-alive
Last-Modified: Fri, 28 Feb 2014 06:54:45 GMT
ETag: "531032b5-c"
Expires: Sat, 01 Mar 2014 10:57:35 GMT
Cache-Control: max-age=86400
Accept-Ranges: bytes

# curl  -I http://192.168.2.15/index.html
HTTP/1.1 200 OK
Server: nginx/1.4.5
Date: Fri, 28 Feb 2014 10:57:41 GMT
Content-Type: text/html
Content-Length: 12
Connection: keep-alive
Last-Modified: Fri, 28 Feb 2014 06:54:45 GMT
ETag: "531032b5-c"
Expires: Sat, 01 Mar 2014 10:57:35 GMT
Cache-Control: max-age=86400
Accept-Ranges: bytes

# curl  -I http://192.168.2.15/index.html
HTTP/1.1 200 OK
Server: nginx/1.4.5
Date: Fri, 28 Feb 2014 10:57:46 GMT
Content-Type: text/html
Content-Length: 12
Connection: keep-alive
Last-Modified: Fri, 28 Feb 2014 06:54:45 GMT
ETag: "531032b5-c"
Expires: Sat, 01 Mar 2014 10:57:35 GMT
Cache-Control: max-age=86400
Accept-Ranges: bytes

上面共請求了3次服務器

192.168.2.15 - - [28/Feb/2014:18:57:35 +0800] "HEAD /index.html HTTP/1.1" 200 0 "-" "curl/7.19.7 (x86_64-redhat-linux-gnu) libcurl/7.19.7 NSS/3.14.0.0 zlib/1.2.3 libidn/1.18 libssh2/1.4.2" "-"
127.0.0.1 - - [28/Feb/2014:18:57:35 +0800] "GET /index.html HTTP/1.0" 200 12 "-" "curl/7.19.7 (x86_64-redhat-linux-gnu) libcurl/7.19.7 NSS/3.14.0.0 zlib/1.2.3 libidn/1.18 libssh2/1.4.2" "192.168.2.15"
192.168.2.15 - - [28/Feb/2014:18:57:41 +0800] "HEAD /index.html HTTP/1.1" 200 0 "-" "curl/7.19.7 (x86_64-redhat-linux-gnu) libcurl/7.19.7 NSS/3.14.0.0 zlib/1.2.3 libidn/1.18 libssh2/1.4.2" "-"
192.168.2.15 - - [28/Feb/2014:18:57:46 +0800] "HEAD /index.html HTTP/1.1" 200 0 "-" "curl/7.19.7 (x86_64-redhat-linux-gnu) libcurl/7.19.7 NSS/3.14.0.0 zlib/1.2.3 libidn/1.18 libssh2/1.4.2" "-"

第一次鏈接192.168.2.15而後轉發給127.0.0.1 返回 HTTP/1.1 200 OK

後面兩次鏈接192.168.2.15沒有轉發給127.0.0.1 直接返回 HTTP/1.1 200 OK

查看緩存目錄,咱們能夠看到生成的緩存文件

# find /tmp/proxy_*
/tmp/proxy_cache_dir
/tmp/proxy_cache_dir/1
/tmp/proxy_cache_dir/1/79
/tmp/proxy_cache_dir/1/79/b47a0009c531900de2a15ba80c0e3791
/tmp/proxy_temp_dir

8.1. gzip 處理

http://localhost/gzip.html 是支持壓縮的,192.168.2.15 proxy_pass http://localhost

# curl -H Accept-Encoding:gzip,defalte  http://localhost/gzip.html

運行後輸出亂碼

# curl -H Accept-Encoding:gzip,defalte  http://192.168.2.15/gzip.html

如今透過反向代理請求試試,你會發現gzip壓縮無效,輸出的是HTML,這是怎麼回事呢?這是由於反向代理不清楚後面的服務器是否支持gzip,因此一概按照正常html請求。如今咱們開啓 gzip_vary on; 每次返回數據會攜帶Vary: Accept-Encoding 頭。

gzip  on;
	gzip_vary on;

reload nginx 後查看Vary: Accept-Encoding輸出

# curl -I http://localhost/gzip.html
HTTP/1.1 200 OK
Server: nginx/1.4.5
Date: Mon, 03 Mar 2014 02:09:16 GMT
Content-Type: text/html
Content-Length: 19644
Last-Modified: Mon, 03 Mar 2014 01:49:02 GMT
Connection: keep-alive
Vary: Accept-Encoding
ETag: "5313df8e-4cbc"
Accept-Ranges: bytes

有 Vary: Accept-Encoding 頭,如今再測試一次

# curl -H "Accept-Encoding: gzip" http://192.168.2.15/gzip.html
<html>
<head>
	<title>Hello</title>

測試失敗,並無出現預期效果,因而到網站找答案,中文與英文資料都看個遍,沒有解決.

最後只能讓反向代理取到數據後再壓縮一次,配置開啓 gzip on;

proxy_temp_path   /tmp/proxy_temp_dir;
proxy_cache_path  /tmp/proxy_cache_dir  levels=1:2   keys_zone=nginx_cache:200m inactive=3d max_size=30g;

server {
    listen       80;
    server_name  192.168.2.15;

	gzip on;
	
	location / {
		proxy_set_header X-Real-IP  $remote_addr;
		proxy_set_header X-Forwarded-For  $proxy_add_x_forwarded_for; 
		# proxy_set_header Accept-Encoding "gzip"; 沒有任何效果
		proxy_pass       http://localhost;
	}
}

Nginx 反向代理做爲代理綽綽有餘,若是作緩存服務器,仍是使用squid, varnish吧。

9. 特殊數據緩存

緩存並不是只能緩存靜態內容,HTML,CSS,JS以及圖片意外的數據同樣能夠緩存。

只要處理好HTTP頭便可。例如Ajax動態內容緩存,JSON數據緩存。

9.1. json

當用戶請求json地址時,咱們將 json 數據附加HTTP頭(Cache-Control, Expires, ETag),而後返回給用戶,用戶的設備會遵循HTTP的聲明,進行緩存操做。

curl -I http://api.example.com/article/json/2/20/0.html
HTTP/1.1 200 OK
Expires: Wed, 26 Aug 2015 05:40:57 GMT
Date: Wed, 26 Aug 2015 05:39:57 GMT
Server: nginx
Content-Type: application/json; charset=utf-8
Transfer-Encoding: chunked
Cache-Control: max-age=60
ETag: 4238111283
Age: 69475
X-Via: 1.1 kaifeng45:3 (Cdn Cache Server V2.0)
Connection: keep-alive

注意這裏使用了僞靜態 /article/json/2/20/0.html 僞靜態與緩存沒有關係,實際起做用的是HTTP頭。

咱們能夠看到 Content-Type: application/json; charset=utf-8 聲明,代表這是json數據,而不是HTML。

9.2. XML

這裏是指動態生成的XML,處理方式與 JSON同樣,XML數據附加HTTP頭(Cache-Control, Expires, ETag)後返回給用戶。

10. 總結

通過詳細的測試咱們發現不一樣的瀏覽器,不一樣的Web服務器,甚至每一個版本都有所差別。

測試總結 Apache HTTPD 最完善 Lighttpd 其次, Nignx仍在快速發展中,Nignx每一個版本差別很大,對HTTP協議實現標準也不太嚴謹,由於Nignx在大陸是趨勢,因此下面給出的例子都是nginx

我比較看好Lighttpd,FastCGI 部分我通常是用php-fpm替代Lighttpd的spawn-fcgi

切記使用Nginx要注意每一個本版細微變化,不然升級後會有影響。我習慣使用yum 安裝 nginx 隨時 yum update 升級。

另外FastCGI 與 mod_php也有所區別

延伸閱讀《 Netkiller Web 手札》http://netkiller.github.io/www/index.html


相關文章
相關標籤/搜索