目錄
Varnish簡介及特點
Varnish日誌
Varnish系統結構
Varnish所支持的算法
VCL介紹
Varnish後端存儲
Varnish實戰
Varnish簡介及特點
Varnish是一款高性能且開源的反向代理服務器和HTTP緩存加速器;在1975年時,儲存媒介只有兩種:內存與硬盤。但現在計算機系統的內存除了主存外,還包括了CPU內的L1、L2,甚至有L3快取。硬盤上也有自己的快取裝置,因此Squid Cache自行處理物件替換的架構不可能得知這些情況而做到最佳化,但操作系統可以得知這些情況,所以這部份的工作應該交給操作系統處理,這就是 Varnish cache設計架構。
其特點如下:
Varnish基於內存緩存,重啓後數據將消失
利用虛擬內存方式,io性能好
支持設置0~60秒內的精確緩存時間
VCL配置管理比較靈活
32位機器上緩存文件大小爲最大2G
具有強大的管理功能,例如top,stat,admin,list等
狀態機設計巧妙,結構清晰
利用二叉堆管理緩存文件,達到積極刪除目的
Varnish日誌
爲了與系統的其它部分進行交互,Child進程使用了可以通過文件系統接口進行訪問的共享內存日誌(shared memory log),因此,如果某線程需要記錄信息,其僅需要持有一個鎖,而後向共享內存中的某內存區域寫入數據,再釋放持有的鎖即可。而爲了減少競爭,每個worker線程都使用了日誌數據緩存。共享內存日誌大小一般爲90M,其分爲兩部分,前一部分爲計數器,後半部分爲客戶端請求的數據。varnish提供了多個不同的工具如varnishlog、varnishncsa或varnishstat等來分析共享內存日誌中的信息並能夠以指定的方式進行顯示。
Varnish系統結構
Varnish主要有兩個進程:Management與Child進程(也稱爲Cache進程);如下圖:
Management進程主要實現了應用新更新的配置,編譯VCL配置、監控Varnish、初始化Varnish及提供一個管理接口等;Management進程會每隔一段時間檢測一下Child進程是否運行正常,如果在指定的時間內沒有響應則Memagement會重啓Child進程。
Varnish所支持的算法
Varnish的director支持的挑選方法中比較簡單的有round-robin和random兩種。其中,round-robin類型沒有任何參數,只需要爲其指定各後端主機即可,挑選方式爲“輪叫”,並在某後端主機故障時不再將其視作挑選對象;random方法隨機從可用後端主機中進行挑選,每一個後端主機都需要一個.weight參數以指定其權重,同時還可以director級別使用.retires參數來設定查找一個健康後端主機時的嘗試次數。在2.1.0以後的版本後,random挑選方法又多了兩種變化形式client和hash。client類型的director使用client.identity作爲挑選因子,這意味着client.identity相同的請求都將被髮送至同一個後端主機。client.identity默認爲cliet.ip,但也可以在VCL中將其修改爲所需要的標識符。類似地,hash類型的director使用hash數據作爲挑選因子,這意味着對同一個URL的請求將被髮往同一個後端主機,其常用於多級緩存的場景中。然而,無論是client還hash,當其傾向於使用後端主機不可用時將會重新挑選新的後端其機。
VCL介紹
VCL是varnish配置緩存策略的工具,它是一種基於“域”(domain specific)的簡單編程語言,可以使用指定運算符:“=”,比較運算符“==”,邏輯運算符“!,&&,!!”等;支持使用正則表達式進行字符串匹配、允許用戶使用set自定義變量、支持if判斷語句,也有內置的函數和變量等;VCL策略在啓用前,會由management進程將其轉換爲C代碼,而後再由gcc編譯器將C代碼編譯成二進制程序。編譯完成後,management負責將其連接至varnish實例,即child進程。
1.vcl_recv()
用於接收和處理請求,當請求到達併成功接收後被調用,通過判斷請求的數據來決定如何處理請求。此函數一般以如下幾個關鍵字結束:
pass:表示進入pass模式,把請求控制權交給vcl_pass函數。
pipe:表示進入pipe模式,把請求控制權交給vcl_pipe函數。
error code [reason]:表示返回“code”給客戶端,並放棄處理該請求,“code”是錯誤標識,例如200、405等等,“reason”是錯誤提示信息。
2.vcl_pipe()
此函數在進入pipe模式時被調用,用於將請求直接傳遞至後端主機,在請求和返回的內容沒有改變的情況下,將不變的內容返回給客戶端,直到這個鏈接關閉。
此函數一般以關鍵字error
code [reason]和pipe結束。
3.vcl_pass()
此函數在進入pass模式時被調用,用於將請求直接傳遞至後端主機,後端主機應答數據後送給客戶端,但不進行任何緩存,在當前連接下每次都返回最新的內容。
此函數一般以關鍵字error code [reason]和pipe結束。
4.lookup
表示在緩存裏查找被請求的對象,並且根據查找的結果把控制權交給vcl_hit()和vcl_miss()
5.vcl_hit()
在執行lookup指令後,如果在緩存中找到請求的內容,將自動調用該函數.
此函數一般以關鍵字error code [reason]、deliver和pass結束。
6.vcl_fetch()
在從後端主機更新緩存並且獲取內容後調用該方法,接着,通過判斷獲取的內容來決定是否將內容放入緩存,還是直接返回給客戶端。一般以關鍵字error code [reason]、deliver和pass結束。
7.vcl_deliver()
在緩存中找到請求的內容後,發送給客戶端前調用此方法。一般以關鍵字error code [reason]、deliver結束。
8.vcl_timeout()
此函數在緩存內容到期前調用。一般以關鍵字discard和fetch結束。
9.vcl_discard()
在緩存內容到期後或緩存空間不夠時,自動調用該方法。一般以關鍵字discard和keep結束。
每種函數的關聯如下圖所示:
Varnish後端存儲
varnish支持多種不同類型的後端存儲,這可以在varnishd啓動時使用-s選項指定。後端存儲包括:
1、file:使用特定的文件存儲全部的緩存數據,並通過操作系統的mmap()系統調用將整個緩存文件映射至內存區域(如果條件允許)
2、malloc:使用malloc()庫調用在varnish啓動時向操作系統申請指定大小的內存空間以存儲緩存對像
varnish無法追蹤某緩存對象是否存入了緩存文件,從而也就無從得知磁盤上的緩存文件是否可用,因此,file存儲方法在varnish停止或重啓時會清除數據。而persistent方法的出現對此有了一個彌補,但persistent仍處於測試階段,例如目前尚無法有效處理要緩存對象總體大小超出緩存空間的情況,所以,其僅適用於有着巨大緩存空間的場景。
選擇使用合適的存儲方式有助於提升系統性,從經驗的角度來看,建議在內存空間足以存儲所有的緩存對象時使用malloc的方法,反之,file存儲將有着更好的性能的表現。然而,需要注意的是,varnishd實際上使用的空間比使用-s選項指定的緩存空間更大,一般說來,其需要爲每個緩存對象多使用差不多1K左右的存儲空間,這意味着,對於100萬個緩存對象的場景來說,其使用的緩存空間將超出指定大小1G左右。另外,爲了保存數據結構等,varnish自身也會佔去不小的內存空間。
Varnish實戰
Varnish安裝,命令使用和配置文件介紹
1、配置yum源,使用yum安裝Varnish # rpm --nosignature -i http://repo.varnish-cache.org/redhat/varnish-3.0/el5/noarch/varnish-release-3.0-1.el5.centos.noarch.rpm # yum -y install varnish 2、查看安裝Varnish所生成的文件 # rpm -ql varnish /etc/rc.d/init.d/varnish #Varnish服務啓動腳本 /etc/rc.d/init.d/varnishlog #Varnish日誌服務啓動腳本 /etc/rc.d/init.d/varnishncsa #Varnish日誌服務啓動腳本 /etc/sysconfig/varnish #Varnish啓動服務的主配置文件 /etc/varnish #配置文件目錄 /etc/varnish/default.vcl #默認VCL文件 /usr/bin/varnish_reload_vcl #Varnish的VCL管理工具 /usr/bin/varnishadm #Varnish管理工具 /usr/bin/varnishhist /usr/bin/varnishlog #Varnish日誌管理工具 /usr/bin/varnishncsa #Varnish日誌管理工具 /usr/bin/varnishreplay /usr/bin/varnishsizes /usr/bin/varnishstat #Varnish狀態查看工具 /usr/bin/varnishtest /usr/bin/varnishtop 3、Varnish服務配置文件 NFILES=131072 MEMLOCK=82000 NPROCS="unlimited" # DAEMON_COREFILE_LIMIT="unlimited" #內核最大打開的文件數 RELOAD_VCL=1 #是否自動加載VCL VARNISH_VCL_CONF=/etc/varnish/default.vcl #默認加載的VCL文件 VARNISH_LISTEN_PORT=80 #監聽端口,默認爲6081 VARNISH_ADMIN_LISTEN_ADDRESS=127.0.0.1 #管理的IP地址 VARNISH_ADMIN_LISTEN_PORT=6082 #管理端口 VARNISH_SECRET_FILE=/etc/varnish/secret #密鑰文件 VARNISH_MIN_THREADS=50 #最小線程數量 VARNISH_MAX_THREADS=1000 #最大線程數量 VARNISH_THREAD_TIMEOUT=120 #線程超時時間 VARNISH_STORAGE_FILE=/var/lib/varnish/varnish_storage.bin #緩存文件位置 VARNISH_STORAGE_SIZE=1G #設置文件緩存大小變量 VARNISH_MEMORY_SIZE=64M #設置內存緩存大小變量 #VARNISH_STORAGE="file,${VARNISH_STORAGE_FILE},${VARNISH_STORAGE_SIZE}" #默認存儲到文件中,這裏可以修改存儲位置 VARNISH_STORAGE="malloc,${VARNISH_MEMORY_SIZE}" #設置緩存位置爲內存 VARNISH_TTL=120 DAEMON_OPTS="-a ${VARNISH_LISTEN_ADDRESS}:${VARNISH_LISTEN_PORT} \ -f ${VARNISH_VCL_CONF} \ -T ${VARNISH_ADMIN_LISTEN_ADDRESS}:${VARNISH_ADMIN_LISTEN_PORT} \ -t ${VARNISH_TTL} \ -w ${VARNISH_MIN_THREADS},${VARNISH_MAX_THREADS},${VARNISH_THREAD_TIMEOUT} \ -u varnish -g varnish \ -S ${VARNISH_SECRET_FILE} \ -s ${VARNISH_STORAGE}" #所有啓動加載選項 4、啓動Varnish [root@varnish ~]# service varnish start [root@varnish ~]# netstat -anput|grep varnish tcp 0 0 0.0.0.0:80 0.0.0.0:* LISTEN 20303/varnishd tcp 0 0 127.0.0.1:6082 0.0.0.0:* LISTEN 20301/varnishd tcp 0 0 :::80 :::* LISTEN 20303/varnishd 5、Varnishd命令 # varnishadm -h varnishadm: invalid option -- 'h' usage: varnishadm [-n ident] [-t timeout] [-S secretfile] -T [address]:port command [...] -n is mutually exlusive with -S and -T 註釋:常用選項 -S:指定密鑰文件 -T:指定服務器地址與端口;如管理一個Varnish服務器,連接到Varnish服務器: # varnishadm -S /etc/varnish/secret -T 127.0.0.1:6082 varnish> help help [command] ping [timestamp] auth response quit #退出 banner status #查看狀態 start #啓動Varnish stop #關閉Varnish vcl.load <configname> <filename> #加載一個VCL文件 vcl.inline <configname> <quoted_VCLstring> vcl.use <configname> #使用VCL文件 vcl.discard <configname> vcl.list #查看VCL文件列表 vcl.show <configname> #查看VCL文件配置 param.show [-l] [<param>] param.set <param> <value> panic.show panic.clear storage.list backend.list backend.set_health matcher state ban.url <regexp> ban <field> <operator> <arg> [&& <field> <oper> <arg>]... ban.list
實驗環境介紹
安裝一個論壇程序,在Varnish服務器上做動靜分離,將動態頁面的請求都轉發到"lamp"服務器組,"lamp"服務器組的數據庫安裝在"lamp1"服務器上面;將靜態頁面轉發到"web"服務器組,對請求的靜態內容做緩存。
安裝LAMP服務並假設論壇
1、在lamp1與lamp2服務器上安裝服務並測試 ######在Lamp1服務器上安裝Httpd、Php、Mysql,啓動服務 [root@lamp1 ~]# yum -yinstall httpd php mysql-server php-mysql [root@lamp1 ~]# servicehttpd start [root@lamp1 ~]# servicemysqld start ------------------------------------------------------------------------ ######在Lamp2服務器上安裝Httpd、Php,啓動服務 [root@lamp2 ~]# yum -yinstall httpd php php-mysql [root@lamp2 ~]# servicehttpd start 2、在Lamp1與Lamp2服務器創建php測試頁面並訪問驗證 [root@lamp1 ~]# echo"<?php phpinfo();" > /var/www/html/index.php
3、安裝論壇程序 點此下載 [root@lamp1 ~]# mysql mysql> create databasebbs; mysql> grant all onbbs.* to 'bbsuser'@'172.16.%.%' identified by 'bbspass'; mysql> flushprivileges; 註釋:爲論壇創建一個數據庫並授權用戶訪問 ------------------------------------------------------------------------ ######安裝論壇程序 [root@lamp1 ~]# unzipDiscuz_X3.0_SC_UTF8.zip [root@lamp1 ~]# cp -rfupload/* /var/www/html/ [root@lamp1 ~]# chmod -R+w /var/www/html/{config,data,uc_server,uc_client} #添加可寫權限 [root@lamp1 ~]# chown -Rapache /var/www/html/* #修改屬主權限 4、訪問"lamp1"服務器地址安裝論壇
[root@lamp1~]# scp -rp /var/www/html/* 172.16.14.3:/var/www/html/ [root@lamp2 ~]# service httpd restart #重啓lamp2服務器的WEB服務
安裝web服務器並創建測試頁訪問
[root@web ~]# yum -y install httpd [root@web ~]# service httpd start [root@web ~]# echo"<h1>WEB</h1>" > /var/www/html/index.html #創建測試頁
Varnish安裝及配置
[root@varnish ~]# cd /etc/varnish/ [root@varnish varnish]# cp default.vcl default.vcl.bak [root@varnish varnish]# vim default.vcl ######定義ACL acl purgers { #定義acl,實現IP地址過濾 "127.0.0.1"; "172.16.0.0"/16; } ######定義健康狀態檢測 probe dynamic { #設置動態網站服務器健康狀態檢測 .url = "/index.html"; .interval = 5s; .timeout = 1s; .expected_response = 200; } #這裏設置了兩個健康狀態檢測主要是爲了區分動、靜網站 probe static { #設置動態網站服務器健康狀態檢測 .url = "/index.html"; #定義檢測的頁面 .interval = 5s; #探測請求的發送週期,默認爲5秒 .timeout = 1s; #每次探測請求的過期時間 .expected_response = 200; } ######定義後端服務器 backend app1 { #定義一個後端服務器 .host = "172.16.14.2"; #服務器地址 .port = "80"; #服務器監聽端口 .probe = dynamic; #健康狀態檢測 } backend app2 { .host = "172.16.14.3"; .port = "80"; .probe = dynamic; } backend web { .host = "172.16.14.4"; .port = "80"; .probe = static; } ######定義後端服務器組,實現負載均衡效果 director apps random { #定義一個後端服務器組,實現負載均衡效果 { .backend = app1; #調用前面已定義過的後端主機 .weight = 2; #設置權重 } { .backend = app2; .weight = 2; } } ######定義vcl_recv函數,實現請求到達併成功接收後調用此函數中定義的規則 sub vcl_recv { ######定義動、靜分離,以".php"或".php?後面跟所有文件"結尾的請求都發送到動態服務器,其他請求都發送到靜態服務器 if (req.url ~ "\.php(\?\.*|$)") { set req.backend = apps; } else { set req.backend = web; } return(lookup); ######定義允許清除緩存的IP地址,調用的是前面定義的ACL if (req.request == "PURGE") { if (!client.ip ~ purgers) { error 405 "Method not allowed"; } return(lookup); } ######重新定義http請求首部,讓後端服務器可以記錄請求客戶端的真實IP地址 if (req.restarts == 0) { if (req.http.x-forwarded-for) { set req.http.X-Forwarded-For = req.http.X-Forwarded-For + ", " + client.ip; } else { set req.http.X-Forwarded-For = client.ip; } } ######除了定義的請求方法外,其他請求都到後端服務器 if (req.request != "GET" && req.request != "HEAD" && req.request != "PUT" && req.request != "POST" && req.request != "TRACE" && req.request != "OPTIONS" && req.request != "DELETE") { return (pipe); } if (req.request != "GET" && req.request != "HEAD") { return (pass); } ######定義不緩存認證與Cookie信息 if (req.http.Authorization || req.http.Cookie) { return (pass); } ######定義壓縮功能 if (req.http.Accept-Enconding) { if (req.url ~ "\.(jpg|jpeg|gif|bmp|png|flv|gz|tgz|tbz|mp3)$") { remove req.http.Accept-Encoding; remove req.http.Cookie; } else if (req.http.Accept-Encoding ~ "gzip") { set req.http.Accept-Encoding = "gzip"; } else if (req.http.Accept-Encoding ~ "deflate") { set req.http.Accept-Encoding = "deflate"; } else { remove req.http.Accept-Encoding; } } ######定義指定格式結尾的文件去除Cookie信息 if (req.request == "GET" && req.url ~ "\.(jpeg|jpg|gif|png|bmp|swf)$") { unset req.http.cookie; } ######定義防盜鏈設置 if (req.http.referer ~ "http://.*") { if (!(req.http.referer ~ "http://.*\.baidu\.com" || req.http.referer ~ "http://.*\.google\.com.*")) { set req.http.host = "www.allen.com"; set req.url = "http://172.16.14.4/error.html"; } } } ######定義vcl_hash函數 sub vcl_hash { hash_data(req.url); if (req.http.host) { hash_data(req.http.host); } else { hash_data(server.ip); } return(hash); } ######定義vcl_hit函數 sub vcl_hit { if (req.request == "PURGE") { #語法方法爲"PURGE" purge; #清除緩存 error 200 "Purged."; #返回錯誤狀態碼爲"200" } return(deliver); } ######定義vcl_miss函數 sub vcl_miss { if (req.request == "PURGE") { purge; error 404 "Not In Cache."; } return(fetch); } ######定義vcl_psss函數 sub vcl_pass { if (req.request == "PURGE") { error 502 "Purged On A Passed Object."; } return(pass); } ######定義vcl_fetch函數 sub vcl_fetch { ######定義緩存,如果匹配到已定義文件結尾的緩存1天,其他則緩存1小時 if (req.request == "GET" && req.url ~ "\.(html|jpg|png|bmp|jpeg|gif|js|ico|swf|css)$") { set beresp.ttl = 1d; set beresp.http.expires = beresp.ttl; } else { set beresp.ttl = 1h; } return(deliver); } ######定義在http首部中,如果請求命中顯示"HIT",未命中則顯示"MISS" sub vcl_deliver { if (obj.hits > 0) { set resp.http.X-Cache = "HIT"; } else { set resp.http.X-Cache = "MISS"; } } >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> [root@varnish ~]# service varnish restart #重啓服務生效,重啓服務器後所有緩存將被清除,不過不用重啓也可以用這種方法使其生效 >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> [root@varnish ~]# varnishadm -S /etc/varnish/secret -T 127.0.0.1:6082 varnish> help #獲取幫助 varnish> vcl.load acl_1 default.vcl #加載acl文件,acl_1爲配置名稱 200 VCL compiled. varnish> vcl.list #查看加載的acl文件列表 200 active 7 boot available 0 acl_1 varnish> vcl.use acl_1 #應用acl文件 200 varnish> quit #退出 >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> 註釋: -S:指定varnish的密鑰文件 -T:指定varnish服務器地址及管理端口,默認端口爲"6082"
驗證服務
1、對服務器做壓力測試 如下: ###後端服務器不經過緩存測試 [root@localhost ~]# ab -c 100 -n 1000 http://172.16.14.2/index.php Concurrency Level: 1000 Time taken for tests: 6.812 seconds Complete requests: 10000 Failed requests: 0 Write errors: 0 Non-2xx responses: 10051 Total transferred: 2281577 bytes HTML transferred: 0 bytes Requests per second: 1468.04 [#/sec] (mean) #每秒請求併發 Time per request: 681.179 [ms] (mean) Time per request: 0.681 [ms] (mean, across all concurrent requests) Transfer rate: 327.10 [Kbytes/sec] received >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> ### [root@localhost ~]# ab -c 1000 -n 10000 http://172.16.14.1/index.php Concurrency Level: 1000 Time taken for tests: 2.594 seconds Complete requests: 10000 Failed requests: 0 Write errors: 0 Non-2xx responses: 10056 Total transferred: 3117360 bytes HTML transferred: 0 bytes Requests per second: 3855.05 [#/sec] (mean) Time per request: 259.400 [ms] (mean) Time per request: 0.259 [ms] (mean, across all concurrent requests) Transfer rate: 1173.59 [Kbytes/sec] received 2、測試緩存是否能命中 [root@lamp2 ~]# curl -I http://172.16.14.1/index.php HTTP/1.1 301 Moved Permanently Server: Apache/2.2.15 (CentOS) X-Powered-By: PHP/5.3.3 location: forum.php Content-Type: text/html; charset=UTF-8 Content-Length: 0 Accept-Ranges: bytes Date: Wed, 09 Oct 2013 01:09:01 GMT X-Varnish: 2142028839 Age: 0 Via: 1.1 varnish Connection: keep-alive X-Cache: MISS #第一次請求,未命中顯示"MISS" >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> [root@lamp2 ~]# curl -I http://172.16.14.1/index.php HTTP/1.1 301 Moved Permanently Server: Apache/2.2.15 (CentOS) X-Powered-By: PHP/5.3.3 location: forum.php Content-Type: text/html; charset=UTF-8 Content-Length: 0 Accept-Ranges: bytes Date: Wed, 09 Oct 2013 01:09:08 GMT X-Varnish: 2142028841 2142028839 Age: 7 Via: 1.1 varnish Connection: keep-alive X-Cache: HIT #第二次請求,命中則顯示"HIT" 3、驗證動、靜分離的效果 [root@web ~]# service httpd stop #停止提供靜態頁面的httpd服務 Stopping httpd: [ OK ] 注意:提供靜態頁面的服務停止後,所有圖片都不能顯示;如果把服務再啓動起來就可以訪問正常了。 4、驗證手動清除緩存 [root@lamp2 ~]# curl -X PURGE http://172.16.14.1/index.php <?xml version="1.0" encoding="utf-8"?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html> <head> <title>200 Purged.</title> </head> <body> <h1>Error 200 Purged.</h1> <p>Purged.</p> <h3>Guru Meditation:</h3> <p>XID: 580728625</p> <hr> <p>Varnish cache server</p> </body> </html> #註釋:已經成功清除緩存 >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> [root@lamp2 ~]# curl -I http://172.16.14.1/index.php HTTP/1.1 301 Moved Permanently Server: Apache/2.2.15 (CentOS) X-Powered-By: PHP/5.3.3 location: forum.php Content-Type: text/html; charset=UTF-8 Content-Length: 0 Accept-Ranges: bytes Date: Wed, 09 Oct 2013 02:03:03 GMT X-Varnish: 580728626 Age: 0 Via: 1.1 varnish Connection: keep-alive X-Cache: MISS