上次寫了一篇數(shù)據(jù)庫緩存撬陵,由于快餐式的風(fēng)格,遭到了廣大讀友的吐槽网缝。上篇風(fēng)格有點“虛”巨税,我本身是一個技術(shù)控,偏向經(jīng)驗/干貨的分享粉臊,本文主要描述靜態(tài)緩存方面的一些心得及分享草添。作為系列二,有所不足之處扼仲,依舊希望大家勇于“亮磚”远寸。
說起靜態(tài)緩存技術(shù),CDN是金典代表之作屠凶。靜態(tài)緩存技術(shù)面非常廣驰后,涉及的開源技術(shù)包含apache、Lighttpd矗愧、nginx灶芝、varnish、squid等。
靜態(tài)緩存监署,一般指web類應(yīng)用中颤专,將圖片、js钠乏、css栖秕、視頻、html等靜態(tài)文件/資源通過磁盤/內(nèi)存等緩存方式晓避,提高資源響應(yīng)方式簇捍,減少服務(wù)器壓力/資源開銷的一門緩存技術(shù)。
本文中主要通過:瀏覽器緩存俏拱、磁盤緩存暑塑、內(nèi)存緩存、nginx的內(nèi)存緩存锅必、CDN五個方面圍繞靜態(tài)緩存而展開事格。
一、瀏覽器緩存
瀏覽器緩存搞隐,也稱為客戶端緩存驹愚,是靜態(tài)緩存中最常見最直接的表現(xiàn)形式,很多時候都往往被人忽略掉劣纲。
案例1:
我們經(jīng)常在nginx的配置文件中看到以下緩存配置:
location ~ .*\.(gif|jpg|jpeg|png|bmp|swf)$
{
expires 1d;
}
location ~ .*\.(js|css)?$
{
expires 15d;
}
案例2:
在經(jīng)常寫jsp的時候逢捺,html標(biāo)簽中關(guān)于http頭信息也可以注意到“expires”的字樣:
<title>My JSP 'index.jsp' starting page</title>
<meta http-equiv="expires" content="60">
<meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
<meta http-equiv="description" content="This is my page">
對于案例1和案例2中(nginx設(shè)置的expires優(yōu)先級大于代碼中設(shè)置的expires優(yōu)先級),expires是給一個資源設(shè)定一個過期時間癞季,也就是說無需去服務(wù)端驗證劫瞳,直接通過瀏覽器自身確認(rèn)是否過期即可,所以不會產(chǎn)生額外的流量绷柒。此種方法非常適合不經(jīng)常變動的資源志于。如果文件變動較頻繁,不要使用expires來緩存废睦。
比如對于常見類web網(wǎng)站來說伺绽,css樣式和js腳本基本已經(jīng)定型,所以最適合的方法是expires來緩存一些內(nèi)容到訪問者瀏覽器郊楣。
案例3:
通過chrome訪問服務(wù)器端的一張圖片憔恳,通過F12鍵打開開發(fā)者前端調(diào)試工具(如圖1):
第一次訪問瓤荔,響應(yīng)200狀態(tài)净蚤,當(dāng)?shù)诙渭昂罄m(xù)訪問的時候,變成304狀態(tài)(如圖2)输硝,客戶端已經(jīng)開始取瀏覽器緩存內(nèi)容今瀑,而不需要去服務(wù)器端獲取對應(yīng)的請求內(nèi)容,即nginx中expires參數(shù)設(shè)置已經(jīng)生效。等待客戶端緩存時間過期后橘荠,會再次請求服務(wù)器端內(nèi)容來更新本地緩存屿附。
介紹到這里,突然想起一個有意思的需求哥童。比如挺份,訪問一張靜態(tài)文件,不想客戶端緩存贮懈,需要每次都去服務(wù)器端取數(shù)據(jù)匀泊。我們可以用“l(fā)ast-modified”參數(shù)來實現(xiàn),即“l(fā)ast-modified”是根據(jù)文件更新時間來確定是否再次發(fā)送加載朵你。
Nginx核心配置如下:
location ~ .*\.(gif|jpg|jpeg|png|bmp|swf)$
{
add_header Last-modified 10;
expires 1d;
}
我們更改掉服務(wù)器傳回客戶端的“l(fā)ast-modified”文件修改時間參數(shù)的值各聘,這樣導(dǎo)致客戶端本地保存的文件時間每次跟服務(wù)器端傳回來的時間不一致,所以每次客戶端“誤認(rèn)為”服務(wù)器端有靜態(tài)文件更新抡医,所以每次都會去服務(wù)器端取“所謂的最新數(shù)據(jù)”躲因。這樣我們可以看到,不管在瀏覽器訪問多少次忌傻,返回的http狀態(tài)都是200大脉,再也找不到304狀態(tài)了。
誤區(qū):在nginx中設(shè)置expires芯勘,并不是指把靜態(tài)內(nèi)容緩存在nginx中箱靴,而是設(shè)置客戶端瀏覽器緩存的時間,這是很多人誤區(qū)所在荷愕。
二衡怀、磁盤緩存
除了存儲在客戶端的靜態(tài)緩存(瀏覽器靜態(tài))技術(shù)外,在服務(wù)器端的靜態(tài)緩存技術(shù)主要分為磁盤緩存類和內(nèi)存緩存類兩大類安疗。單純圍繞nginx的squid抛杨、varnish等一類中間件,處理靜態(tài)數(shù)據(jù)的性能十分優(yōu)秀荐类。核心是nginx基于epoll網(wǎng)絡(luò)模型怖现,而相比apache基于select網(wǎng)絡(luò)模型。所以apache的優(yōu)勢在于密計算型玉罐,穩(wěn)定性好屈嗤。而nginx偏向靜態(tài)處理,反向代理吊输,高并發(fā)饶号。比如apache+php的穩(wěn)定性比nginx+php要好,而性能是明顯nginx要優(yōu)秀許多季蚂。
以上僅單純是對磁盤中靜態(tài)數(shù)據(jù)處理的能力茫船,所謂磁盤緩存琅束,指另外的一種緩存靜態(tài)文件的技術(shù)。以nginx配置為例:
#levels設(shè)置目錄層次
#keys_zone設(shè)置緩存名字和共享內(nèi)存大小
#inactive在指定時間內(nèi)沒人訪問則被刪除在這里是1天
#max_size最大緩存空間
proxy_cache_path /alidata/www/default/cache_dir/ levels=1:2 keys_zone=cache_one:200m inactive=1d max_size=30g;
server {
listen 82;
server_name _;
location ~ .*\.(gif|jpg|jpeg|png|bmp|swf|html)$
{
proxy_cache cache_one; # keys_zone后的內(nèi)容對應(yīng)
proxy_cache_valid 200 304 301 302 10d; #哪些狀態(tài)緩存多長時間
proxy_cache_valid any 1d; #其他的緩存多長時間
proxy_cache_key $host$uri$is_args$args; #通過key來hash算谈,定義KEY的值
proxy_pass http://10.168.247.180:81;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
access_log /alidata/nginx/logs/default-cache.log;
可以看出nginx主要通過proxy_cache來實現(xiàn)web cache涩禀,熟悉nginx的同學(xué),不難看出然眼,以上配置在location這里艾船,不僅可以實現(xiàn)靜態(tài)文件的緩存,還可以實現(xiàn)動態(tài)文件的緩存(這里放在下章節(jié)詳細(xì)介紹)高每。我們編寫個test.html測試文件丽声,然后并訪問。test.html源碼如下:
<html>
<head>
<title>test cache</title>
<meta http-equiv="expires" content="-1">
</head>
<body>
<br>
![](eg_tulip.jpg)
</body>
</html>
我們發(fā)現(xiàn)服務(wù)器的cache目錄里面觉义,多了兩個緩存文件:
/alidata/www/default/cache_dir/c/68/b0ad5d3e7f099bfff9e4fc6a159d868c
/alidata/www/default/cache_dir/f/fa/53edc39ed253e14415a29412cfc01faf
有意思的雁社,這兩個文件里面的內(nèi)容分別為(通過less命令查看):
所以不難看出,nginx把html內(nèi)容和圖片二進(jìn)制全部緩存到本地磁盤上了晒骇。下次用戶再次來訪問test.html的時候霉撵,nginx直接將緩存在本地磁盤的文件返回給用戶。特別是后端如若是部署的tomcat洪囤、iis等徒坡,nginx強(qiáng)大的靜態(tài)緩存能力,有效減少了服務(wù)器壓力瘤缩。
三喇完、內(nèi)存緩存
緊接上面描述的磁盤緩存,內(nèi)存緩存顧名思義剥啤,就是把靜態(tài)文件緩存在服務(wù)器端的內(nèi)存中锦溪。所以這種緩存,如若命中緩存的話府怯,取內(nèi)存中的緩存數(shù)據(jù)返回比取磁盤中的緩存數(shù)據(jù)返回刻诊,性能要高很多。以varnish為例牺丙,varnish核心配置如下:
啟動命令:
varnishd -f default.vcl -s malloc,2G -a 0.0.0.0:80 -w 1024,51200,10 -t 3600 -T 192.168.100.2:3500
參數(shù)簡介:
-a address:port 監(jiān)聽端口
-f 指定配置文件
-s 指定緩存類型 malloc為內(nèi)存则涯, file 文件緩存
-t 默認(rèn)TTL
-T address:port 管理端口
-w 最小線程,最大線程冲簿,超時時間
default.vcl核心配置如下:
sub vcl_fetch {
if (req.request == "GET" && req.url ~ "\.(gif|jpg|jpeg|png|bmp|swf|html)$") {
set obj.ttl = 3600s;
}
}
Varnish對.gif粟判、.jpg、.jpeg峦剔、.png等結(jié)尾的URL緩存時間設(shè)置1小時档礁。varnish設(shè)置完畢后,我們用命令行方式羊异,通過查看網(wǎng)頁頭來查看命中情況:
[root@test-varnish ~]# curl -I http://***.***.***.***/test.html
HTTP/1.1 200 OK
Server: Apache/2.2.27 (Unix) PHP/5.3.18 mod_perl/2.0.4 Perl/v5.10.1
Last-Modified: Sat, 10 Jul 2016 11:25:15 GMT
ETag: "55dh9-233f-33d23c3758dg2"
Content-Type: text/html
Content-Length: 23423
Date: Fri, 18 May 2016 21:29:16 GMT
X-Varnish: 1364285597
Age: 0
Via: 1.1 varnish
Connection: keep-alive
X-Cache: MISS from ***.***.***.*** #這里的“MISS”表示此次訪問沒有從緩存讀取
[root@test-varnish ~]# curl -I http://***.***.***.***/test.html
HTTP/1.1 200 OK
Server: Apache/2.2.27 (Unix) PHP/5.3.18 mod_perl/2.0.4 Perl/v5.10.1
Last-Modified: Sat, 10 Jul 2010 11:25:15 GMT
ETag: "55dh9-233f-33d23c3758dg2"
Content-Type: text/html
Content-Length: 23423
Date: Fri, 18 May 2016 21:40:23 GMT
X-Varnish: 1364398612 1364285597
Age: 79
Via: 1.1 varnish
Connection: keep-alive
X-Cache: HIT from ***.***.***.*** #由“HIT”可知事秀,第二次訪問此頁面時,從緩存中讀取內(nèi)容野舶,也就是緩存命中
最后易迹,我們可以通過varnishadm命令來清理緩存,也可以通過varnishstat命令來查看varnish系統(tǒng)緩存狀態(tài)平道。
四睹欲、Nginx的內(nèi)存緩存
以上主要以Varnish為例,介紹了內(nèi)存緩存靜態(tài)資源的方法一屋。其實nginx也有內(nèi)存緩存窘疮,相比squid、varnish而言冀墨,nginx的內(nèi)存緩存需要通過編碼實現(xiàn)闸衫。如下配置:
location /images/ {
set $memcached_key $request_uri;
add_header X-mem-key $memcached_key;
memcached_pass 127.0.0.1:11211;
default_type text/html;
error_page 404 502 504 = @app;
}
memcached_pass指定服務(wù)器地址,使用變量$memcache_key為key查詢值诽嘉,去memcache查詢對應(yīng)value值蔚出。
如我們訪問:http://.../image/test.jpg ,則nginx去memcache中查詢key為“test.jpg”的value值并返回。如果沒有相應(yīng)的值虫腋,則返回error_page 404骄酗。介紹到這里,關(guān)鍵在于存儲在memcache中的靜態(tài)文件悦冀,需要通過代碼寫入memcache中趋翻。怎么樣通過php/java等代碼把靜態(tài)資源的數(shù)據(jù)寫入memcache中,關(guān)于這塊的示例就不再過多介紹盒蟆。
Nginx的內(nèi)存緩存因為需要通過編碼實現(xiàn)踏烙,所以靈活性特別高。這塊可以結(jié)合自身業(yè)務(wù)系統(tǒng)的特點历等,讓靜態(tài)緩存的靈活性和效率都能得到保障宙帝。可能唯一的缺陷就是募闲,通過編碼實現(xiàn)的方式步脓,給我們維護(hù)管理帶來了負(fù)擔(dān)。在之前我曾參與的一個電商系統(tǒng)浩螺,就是把客戶的訂單照片通過php代碼寫入memcache靴患,客戶訪問取圖的時候,從memcache中獲取要出,速度效率特別高鸳君。Nginx作為一款在七層無所不能且輕量級高性能的中間件,能夠直接去memcache中取數(shù)據(jù)患蹂,來實現(xiàn)靜態(tài)緩存的效果或颊,這塊相應(yīng)的功能是其他軟件無法相媲美的砸紊。
五、CDN
說起CDN囱挑,大家都不陌生醉顽,它是靜態(tài)緩存加速最典型的代表。CDN技術(shù)并不是一門新的技術(shù)平挑,它是基于傳統(tǒng)nginx游添、squid、varnish等web 緩存技術(shù)通熄,結(jié)合DNS智能解析的靜態(tài)緩存加速技術(shù)唆涝。值得注意的是,他對動態(tài)鏈接訪問并沒有加速效果唇辨。架構(gòu)原理圖如下圖5:
所以CDN的靜態(tài)緩存技術(shù)核心主要在于兩點:
節(jié)點緩存:對需要加速的網(wǎng)站應(yīng)用廊酣,相應(yīng)的靜態(tài)資源通過內(nèi)存緩存+磁盤緩存的方式緩存在服務(wù)器端。
精準(zhǔn)調(diào)度:對訪問的用戶ip進(jìn)行智能解析調(diào)度赏枚,實現(xiàn)就近緩存節(jié)點訪問啰扛。比如以上圖例中,北京用戶訪問www.a.com嗡贺。通過dns解析的時候隐解,分析用戶ip,發(fā)現(xiàn)是北京用戶诫睬。則dns返回對應(yīng)北京緩存節(jié)點的ip地址給到用戶煞茫,則用戶www.a.com,則默認(rèn)訪問北京服務(wù)器上面的緩存數(shù)據(jù)摄凡,實現(xiàn)就近訪問的策略续徽,大大提升了訪問速度。
我為自己帶鹽亲澡,原創(chuàng)作者:喬銳杰