緩存的基礎(chǔ)知識(shí)
1、程序本身具有局部性
-
時(shí)間局部性
- 過去訪問到的數(shù)據(jù)题翰,也有可能被兩次訪問
-
空間局部性
- 一個(gè)數(shù)據(jù)被訪問到時(shí)恶阴,離它最近的文件可能馬上也會(huì)被訪問
2、命中率
-
文檔命中率
- 從文檔個(gè)數(shù)進(jìn)行衡量
-
字節(jié)命中率
- 從內(nèi)容大小進(jìn)行衡量
3豹障、緩存系統(tǒng)的特性
-
緩存對(duì)象
- 有生命周期冯事,且是定期清理的
-
緩存空間耗盡
- 使用LRU(最近最少使用算法)或者M(jìn)RU算法進(jìn)行緩存項(xiàng)清理
-
不可緩存項(xiàng)
- 用戶私有數(shù)據(jù)
4、緩存系統(tǒng)一般處理步驟
-
接收請(qǐng)求
-
解析請(qǐng)求
- 提取請(qǐng)求的URL及各種首部
-
查詢緩存
-
新鮮度檢測(cè)
-
創(chuàng)建響應(yīng)報(bào)文
-
發(fā)送響應(yīng)報(bào)文
-
記錄日志
5血公、新鮮度檢測(cè)機(jī)制
-
過期日期
-
HTTP/1.0
: expires(其是一個(gè)絕對(duì)時(shí)間) -
HTTP/1.1
: Cache-Control: max-age=600(其是一個(gè)相對(duì)時(shí)間)
-
-
產(chǎn)效性再驗(yàn)證(revalidate)
- 如果原始內(nèi)容未改變昵仅,則僅響應(yīng)首部信息,響應(yīng)碼為304(not modified)
- 如果原始內(nèi)容發(fā)生了改累魔,則正常響應(yīng)摔笤,響應(yīng)碼為200
- 3)如果原始內(nèi)容消失,則響應(yīng)404垦写,此時(shí)緩存中的cache object也應(yīng)該被刪除
-
條件式請(qǐng)求方式
-
If-Modified-Since
: 基于請(qǐng)求內(nèi)容的時(shí)間戳作驗(yàn)證 If-unmodified-Since
If-Match
-
If-None-Match
: 結(jié)合Etag對(duì)文件做MD5校驗(yàn)
-
6吕世、HTTP緩存相關(guān)首部
age
: 一個(gè)緩存對(duì)象從產(chǎn)生到此刻為止,經(jīng)過的多少時(shí)間-
Cache-Control請(qǐng)求首部
-
no-cache
: 不要從緩存中返回內(nèi)容 -
max-age
: 相對(duì)過期時(shí)間梯投,是以秒為單位 -
max-stale
: 可以接受的對(duì)象命辖,但是過期時(shí)間必須小于max-stale值 -
min-fresh
: 接受其新鮮生命期大于其當(dāng)前age跟min-fresh值之各的緩存對(duì)象
-
-
Cache-Control響應(yīng)首部
-
no-cache
: 可以緩存,但要跟web服務(wù)器再驗(yàn)證 -
no-store
: 不允許緩存 -
public
: 可以用cache內(nèi)容回應(yīng)任何用戶 -
private
: 只能用緩存內(nèi)容回應(yīng)先前請(qǐng)求該內(nèi)容的那個(gè)用戶 -
max-age
: 本響應(yīng)包含的對(duì)象的過期時(shí)間 -
s-maxage
: 公共緩存的最大生命周期 -
must-revlidate
: 每次響應(yīng)給客戶端時(shí)分蓖,必須做有效性驗(yàn)證
-
緩存時(shí)需要考慮到的特殊首部
Authorization:跟授權(quán)相關(guān)的首部
cookie:用戶識(shí)別相關(guān)的首部
Vary:accept-encoding:所能接受的字符編碼格式
以上三種首部未特性情況下是不予緩存
通常與緩存相關(guān)的方法:
1尔艇、GET
2、HEAD
7么鹤、常見的緩存服務(wù)開源解決方案
-
varnish
: 專用于web服務(wù)的緩存 -
squid
: 類似nginx,apache终娃,但比varnish穩(wěn)定
Varnish
varnish對(duì)比squid的優(yōu)點(diǎn)
- 1、varnish的穩(wěn)定性很高午磁,兩者在完成相同負(fù)荷的工作時(shí)尝抖,squid服務(wù)器發(fā)生故障的幾率要高于varnish,因?yàn)閟quid要經(jīng)常重啟
- 2、varnish訪問速度更快迅皇,其采用了"Visual Page Cache"技術(shù)昧辽,所有緩存數(shù)據(jù)都直接從內(nèi)存中讀取,而squid是從硬盤讀取登颓,因而varnish在訪問速度方面會(huì)更快
- 3顶瞒、varnish可以支持更多的并發(fā)連接香璃,因?yàn)関arnish的TCP連接釋放要比squid快尤筐,因而在高并發(fā)連接情況下可以支持更多TCP連接
- 4俩垃、varnish可以通過管理端口,使用正則表達(dá)式批量的清除部分緩存瘤载,而squid是做不到的。
- 5、squid屬于單進(jìn)程使用單核CPU塞栅,但Varnish是通過fork形式打開多進(jìn)程來做處理,所以是合理的使用所有核來處理相應(yīng)的請(qǐng)求
varnish對(duì)比squid的缺點(diǎn)
- 1腔丧、varnish進(jìn)程一旦Hang放椰、Crash或者重啟,緩存數(shù)據(jù)都會(huì)從內(nèi)存中完全釋放愉粤,此時(shí)所有請(qǐng)求都會(huì)發(fā)送到后端服務(wù)器砾医,在高并發(fā)情況下,會(huì)給后端服務(wù)器造成很大的壓力
- 2衣厘、在varnish使用中如蚜,如果單個(gè)vrl的請(qǐng)求通過HA/F5,每次請(qǐng)求不同的varnish服務(wù)器時(shí)影暴,被請(qǐng)求的varnish服務(wù)器都會(huì)被穿透到后端错邦,而同樣的請(qǐng)求會(huì)在多臺(tái)服務(wù)器上緩存 ,也會(huì)造成varnish的緩存資源浪費(fèi)坤检,也會(huì)造成性能下降
varnish的工作進(jìn)程特性
-
varnish工作進(jìn)程示意圖
*
Management(主進(jìn)程)
* 實(shí)現(xiàn)應(yīng)用新的配置
* 編譯VCL
* 監(jiān)控Varnish的子進(jìn)程(其management每隔幾秒進(jìn)行子進(jìn)程探測(cè)兴猩,如較長時(shí)間沒有回應(yīng)探測(cè)它將重啟一個(gè)子進(jìn)程)
* 初始化varnish
* 提供命令行接口
*
Child/Cache
* accept : 接收新的連接請(qǐng)求,交由worker線程處理
* worker : 用于處理并響應(yīng)用戶請(qǐng)求
* expiry : 管理過期緩存早歇,從緩存中清理過期的Cache
*
Vcl compiler
* 把配置文件編譯成VCL格式
*
C compiler
* C編譯器倾芝,vcl compiler調(diào)用c compiler
*
日志
shared memory log,共享內(nèi)存日志大小默認(rèn)一般為90M+箭跳,分為兩部分組成晨另,前一部分為計(jì)數(shù)器,后一部分請(qǐng)求響應(yīng)的相關(guān)數(shù)據(jù)谱姓,日志保存在一個(gè)共享的內(nèi)存空間借尿,只能保存最近最新的日志,需要使用工具屉来,把日志不斷的導(dǎo)出路翻,以實(shí)現(xiàn)長期保存
* `varnishlog` : 其以守護(hù)進(jìn)程方式運(yùn)行,需要將其重啟才會(huì)把日志導(dǎo)入到本地磁盤茄靠,類似于httpd日志的comm格式
* `varnishncsa` : 其與varnishlog類似茂契,但日志的格式與httpd的combind格式類似
-
varnish的進(jìn)程工作特性
- varnish啟動(dòng)或有2個(gè)進(jìn)程master(management)進(jìn)程和child(worker)進(jìn)程,master讀入存儲(chǔ)配置命令慨绳,進(jìn)行初始化掉冶,然后fork并監(jiān)控child,child則分配線程進(jìn)行cache工作真竖,child還會(huì)做管理線程生成很多的worker線程
- child線程主線程初始化過程中,將存儲(chǔ)大文件整個(gè)加載到內(nèi)存中厌小,如果該文件超出系統(tǒng)的虛擬內(nèi)存恢共,則會(huì)減少原來配置MMAP大小,然后繼續(xù)加載璧亚,這時(shí)候創(chuàng)建并初始化空間存儲(chǔ)結(jié)構(gòu)體讨韭,放在存儲(chǔ)管理的struct中,等待分配
- 接著varnish某個(gè)負(fù)責(zé)接受http連接的線程開始等待用戶請(qǐng)求癣蟋,如果有新http連接拐袜,但這個(gè)線程只負(fù)責(zé)接收,然后喚醒等待線程池中的work線程梢薪,進(jìn)行請(qǐng)求處理
- work線程讀入uri后,將會(huì)查找已有的object,命中直接返回尝哆,沒有命中則會(huì)從后端服務(wù)器中取出來秉撇,放到緩存中,如果緩存已滿秋泄,會(huì)根據(jù)LRU算法釋放舊的Object琐馆,對(duì)于釋放緩存,有一個(gè)超時(shí)線程檢測(cè)緩存中所有object的生命周期恒序,如果緩存過期(ttl),則刪除瘦麸,釋放相應(yīng)的存儲(chǔ)內(nèi)存
varnish使用單進(jìn)程多線程模型,其worker stats類似于一個(gè)線程池歧胁,所有的資源將整合在一個(gè)工作區(qū)中滋饲,以降低線程在申請(qǐng)或修改內(nèi)存時(shí),出現(xiàn)的競(jìng)爭(zhēng)的可能性喊巍,當(dāng)多個(gè)線程同時(shí)訪問同個(gè)資源時(shí)屠缭,工作區(qū)對(duì)資源以施加鎖保證用戶的請(qǐng)求在資源爭(zhēng)用時(shí),后來的線程處于等待狀態(tài)崭参,以協(xié)調(diào)線程的工作呵曹。
-
varnish存儲(chǔ)緩存機(jī)制
-
malloc
基于內(nèi)存存儲(chǔ),在內(nèi)存中存儲(chǔ)各緩存對(duì)象何暮,時(shí)間久了會(huì)產(chǎn)生緩存碎片奄喂,如果分配的內(nèi)存太大,會(huì)降低效率海洼。varnish可能會(huì)激活大內(nèi)存空間分配機(jī)制(在Centos6以后)跨新,這樣也會(huì)降低緩存的查詢效率
-
file
所有緩存對(duì)象緩存在單個(gè)文件中,重啟后將會(huì)失效贰军,不支持持久機(jī)制玻蝌,建議使用SSD存放緩存數(shù)據(jù)蟹肘,一般用于大文件緩存,如圖片等
-
persistent
基于文件的持久存儲(chǔ)俯树,varnish重啟了帘腹,緩存還有效,其目前為實(shí)驗(yàn)性項(xiàng)目许饿,生產(chǎn)環(huán)境中不能使用
-
-
varnish內(nèi)存分配回收機(jī)制
-
分配
- malloc()函數(shù)
- jemalloc()函數(shù) : 其是malloc的并發(fā)實(shí)現(xiàn)
-
回收
- free()函數(shù)
-
-
VCL編程語法
VCL:varnish configuration language阳欲,又被稱之為DSL(域)編譯語言,其是參照C和perl語言的風(fēng)格編寫陋率,基本格式如下:
-
sub NAME {
....;
-
}
* 不支持循環(huán)
* 受狀態(tài)引擎的變量球化,變量的可調(diào)用位置與state engine有密切相關(guān)性
* 支持終止語句,使用ruturn()返回一個(gè)action,其沒有返回值
* 可自定義變量
* //,#,/* */: 用于注釋瓦糟,會(huì)被編譯器忽略
* “域”專用筒愚,只能一個(gè)域有效
* 操作符: `=,==,~,!,&&,||`
* 條件判斷語句的寫法:
```
單分支:
if (condition) {
....;
} else {
....;
}
多分支:
if (condition) {
...;
} elseif {
...;
} else {
...;
}
```
* 變量賦值:`set name = value`
* 撤消變量的值:`unset name`
安裝及配置Varnish
Centos 6
-
安裝
-
yum install varnish
: 默認(rèn)安裝為varnish2的版本
-
-
主進(jìn)程配置文件
/etc/sysconfig/varnish
-
/etc/varnish/default.vcl
: VCL引擎配置文件
-
服務(wù)管理腳本
-
/etc/rc.d/init.d/varnish
: varnish服務(wù)主進(jìn)程管理 -
/etc/rc.d/init.d/varnishlog
: varnishlog服務(wù)管理 -
/etc/rc.d/init.d/varnishncsa
: varnishncsa服務(wù)管理
-
Centos 7
-
安裝
-
yum install varnish
: 默認(rèn)安裝為varnish 4的版本
-
-
主進(jìn)程配置文件
-
/etc/varnish/varnish.params
: varnish服務(wù)主進(jìn)程管理 -
/etc/varnish/default.vcl
: VCL引擎配置文件
-
-
服務(wù)管理腳本
/usr/lib/systemd/system/varnish.service
/usr/lib/systemd/system/varnishlog.service
/usr/lib/systemd/system/varnishncsa.service
varnish主配置文件參數(shù)說明
-
RELOAD_VCL=1
: 是否啟動(dòng)加載VCL配置文件 -
VARNISH_VCL_CONF=/etc/varnish/default.vcl
: varnish的VCL配置文件路徑 -
VARNISH_LISTEN_PORT=6081
: 默認(rèn)監(jiān)聽端口 -
VARNISH_ADMIN_LISTEN_ADDRESS=127.0.0.1
: 遠(yuǎn)程管理監(jiān)聽地址 -
VARNISH_ADMIN_LISTEN_PORT=6082
: 遠(yuǎn)程管理監(jiān)聽端口 -
VARNISH_SECRET_FILE=/etc/varnish/secret
: varnish默認(rèn)加載的密鑰文件,其為遠(yuǎn)程連接varnish的共享密鑰文件 -
VARNISH_MIN_THREADS=50
: varnish最少啟動(dòng)worker線程 -
VARNISH_MAX_THREADS=1000
: varnish最大啟動(dòng)worker線程數(shù)(據(jù)說超出5000就不穩(wěn)定了) -
VARNISH_THREAD_TIMEOUT=120
: 空閑線程的超時(shí)時(shí)間菩浙,超時(shí)后被父進(jìn)程銷毀 -
VARNISH_STORAGE_FILE=/var/lib/varnish/varnish_storage.bin
: varnish的緩存存儲(chǔ)位置文件巢掺,其是二進(jìn)制格式 -
VARNISH_STORAGE_SIZE=1G
: 緩存大小 -
VARNISH_STORAGE="file,${VARNISH_STORAGE_FILE},${VARNISH_STORAGE_SIZE}”
: 基于文件存儲(chǔ)緩存的設(shè)置方式 -
VARNISH_STORAGE_SHM=64M
: 基于內(nèi)存的存儲(chǔ)緩存,指定緩存的大小劲蜻,不可與文件的方式共存 -
VARNISH_STORAGE=“malloc,${VARNISH_STORAGE_SHM}”
: varnish基于內(nèi)存存儲(chǔ)緩存的設(shè)置方式
注意:varnish配置文件修改不應(yīng)重啟服務(wù)陆淀,而是手動(dòng)加載配置文件
Varnish管理工具
varnishd命令
其可以在/etc/sysconfig/varnish的配置文件中完成
-
Options
-
-f
: vcl的配置文件 -
-a address[:port]
:服務(wù)監(jiān)聽的IP地址及端口 -
-s[name=]type[,options]
: varnish緩存的存儲(chǔ)機(jī)制- options
- malloc[,size]
- file[,path[,size[,granularity]]] (力度)
- persistent,path,size
- options
-
-T address[:port]
: 指定管理接口,默認(rèn)6082 -
-p param=value
: 指定參數(shù) -
-r param
: 指定只讀參數(shù)
-
2先嬉、varnishadm命令
其是通過連接varnish服務(wù)端轧苫,可以實(shí)現(xiàn)varnish服務(wù)的管理操作,實(shí)現(xiàn)動(dòng)態(tài)加載VCL的配置文件
-
Options:
-
-S scret_file
: 密鑰文件疫蔓,跟服務(wù)啟動(dòng)的一樣含懊,在varnish目錄下存放 -
-T IP:PORT
: 連接服務(wù)端的端口,默認(rèn)為6082
-
-
varnish命令行命令
-
vcl.list
: 查看vcl配置文件的列表 -
vcl.load NAME default.vcl
: 加載vcl當(dāng)前的配置文件為NAME -
vcl.use NAME
: 使用加載的配置文件 -
vcl.show NAME
: 查看加載的配置文件詳細(xì)配置信息 -
vcl.discard
: 刪除加載的配置文件 -
backend.list
: 查看后端服務(wù)器列表及狀態(tài) -
param.show -l
: 查看varnish主進(jìn)程的運(yùn)行選項(xiàng)參數(shù) -
param.set <param> <value>
: 在運(yùn)行時(shí)設(shè)定主進(jìn)程的運(yùn)行參數(shù) -
ping
: 判斷后端服務(wù)器的狀態(tài) -
status
: 查看進(jìn)行的運(yùn)行狀態(tài) -
panic.show
: 如果某個(gè)子進(jìn)程曾經(jīng)panic過衅胀,可以使用這個(gè)命令查看(panic本為恐慌绢要,代表服務(wù)曾經(jīng)崩潰過) -
storage.list
: 查看緩存存儲(chǔ)機(jī)制 -
ban <field> <operator> <arg> [&& <field> <oper> <arg> ]….
: 手動(dòng)清除緩存 -
ban.list
: 列出清除緩存的規(guī)則列表,由上一個(gè)參數(shù)定義
-
3拗小、Varnishtop命令
內(nèi)存日志區(qū)域查看工具
-
Options
-
-I REGEXP
: 僅顯示被模式匹配到的條目重罪,過濾右邊的信息條目 -
-X
: 僅顯示不被模式匹配到的條目,過濾右邊的信息條目 -
-i
: 過濾左邊字段 -
-x
: 指定的不顯示哀九,不指定的才顯示 -
-C
: 忽略字符大小寫 -
-d
: 顯示已有的日志
varnishtop -i RxHeader varnishtop -I ^User-Agent varnishtop -I ^User-Agent -1 : 只顯示一屏即退出
-
4剿配、varnishstat命令
varnish的運(yùn)行統(tǒng)計(jì)數(shù)據(jù)
-
Options
-
-f filed,filed,....
: 指明只顯示哪些參數(shù) -
-l
: 列出所有字段 -
-x
: 以XML格式輸出 -
-j
: 以json格式輸出
-
5、varnishlog命令
啟動(dòng)以comm格式記錄日志到磁盤
6阅束、varnishncsa命令
啟動(dòng)以combind格式記錄日志到磁盤
VCL Engine
vcl engine是varnish通過VCL配置語言定義的緩存策略呼胚,state engine之間有相關(guān)性,上級(jí)engine通過return指明下級(jí)engine息裸,常用的引擎(varnish version 3),如下:
-
vcl_recv
- 由return(lookup)定義到vcl_hash引擎
-
vcl_hash
- 其是對(duì)緩存項(xiàng)進(jìn)行hash計(jì)算
-
vcl_fetch
- 其是向后端服務(wù)器取文件的
-
vcl_hit
- 其下一個(gè)工作引擎可能是deliver,也有可能是pass(為pass的情況下:命中后需要把緩存強(qiáng)制刪除)
vcl_miss
vcl_deliver
-
vcl_pipe
- 無法識(shí)別的方法蝇更,交由pipe管道引擎處理
-
vcl_pass
- 可以理解的方法沪编,但不緩存,就經(jīng)由pass
vcl_error
Vcl engine 完整的工作流程示意圖(1)
Vcl engine 完整的工作流程示意圖(2)
Vcl engine常見工作流程
1年扩、查詢緩存未命中的工作流
2蚁廓、查詢緩存命中的工作流
3、未識(shí)別的HTTP方法工作流
4厨幻、不予緩存的工作流
5相嵌、完整的工作流
-
restart
- 當(dāng)命中后,把url進(jìn)行了重寫操作况脆,就需要從vcl_recv重新開始饭宾,即為重啟,這類操作被稱之為restart,但varnish有一個(gè)內(nèi)申機(jī)制格了,即重啟了10次看铆,仍然還在重啟,此次請(qǐng)求將會(huì)被丟棄
-
error
- 類似于404類的請(qǐng)求錯(cuò)誤
6盛末、狀態(tài)引擎說明
- 1性湿、首先由vcl_init裝載vcl引擎
- 2、再由vcl_recv將請(qǐng)求接入满败,分析是否服務(wù)于此請(qǐng)求,并指定如何服務(wù)叹括,可能交由下一個(gè)(pipe,pass,hash)處理
- 3算墨、vcl engine可用的return函數(shù)
-
return(lookup)
: 送給vcl_hash引擎處理 -
return(pass)
: 交給vcl_pass引擎處理 -
return(pipe)
: 交由vcl_pipe引擎處理 -
error CODE
: 返回錯(cuò)誤和相應(yīng)的CODE給客戶端 -
return(deliver)
: 交由vcl_deliver直接投遞 return(hit_for_pass)
-
return(restart)
: 重啟請(qǐng)求
-
VCl引擎中常用變量
-
1、在任何引擎中均可使用
-
now
: 獲取當(dāng)前系統(tǒng)當(dāng)前時(shí)間 -
.host
: 后端主機(jī)或主機(jī)名 -
.port
: 后端主機(jī)的端口或服務(wù)名
-
-
2汁雷、用于處理一個(gè)請(qǐng)求階段
可用于recv,hash,pipe,pass引擎中净嘀。
* `client.ip` : 客戶端IP地址
* `server.hostname` : 服務(wù)器的主機(jī)名(緩存服務(wù)器)
* `server.ip` : varnish服務(wù)器的IP
* `server.port` : varnish服務(wù)器的端口
* `req.request` : 客戶端的請(qǐng)求方法
* `req.url` : 客戶端請(qǐng)求的URL
* `req.proto` : http協(xié)議版本
* `req.backend` : 用于服務(wù)此次請(qǐng)求的后端主機(jī)
* `req.backend.healthy` : 后端主機(jī)的健康狀態(tài)
* `req.http.HEADER` : 引用請(qǐng)求報(bào)文中指定的首部,哪req.http.host
* `req.hash_always_miss`
* `req.hash_ignore_busy`
* `req.can_gzip` : 客戶端是否能夠接受GZIP壓縮格式的響應(yīng)內(nèi)容
* `req.restarts` : 此請(qǐng)求被重啟的次數(shù)
-
3侠讯、vanish向backend主機(jī)發(fā)起請(qǐng)求前可用的變量
-
bereq.request
: 請(qǐng)求方法 bereq.url
bereq.proto
bereq.http.HEADER
-
bereq.connect_timeout
: 等待與后端建立連接的超時(shí)時(shí)長
-
-
4挖藏、當(dāng)后端服務(wù)器響應(yīng)varnish,但未放置緩存之前
-
beresp.do_stream
: 表示流式響應(yīng)
流式響應(yīng):當(dāng)后端backend主機(jī)響應(yīng)一個(gè)10M大小的文件厢漩,10M的文件是由多個(gè)數(shù)據(jù)報(bào)文組成膜眠,當(dāng)varnish接收到一個(gè)報(bào)文時(shí),就直接將報(bào)文發(fā)給客戶端溜嗜,也不是等待數(shù)據(jù)報(bào)文接收完整后再發(fā)出宵膨,這就被稱之為流式響應(yīng)
-
beresp.do_gzip
: 從后端服務(wù)器收到的響應(yīng)報(bào)文,要不要壓縮以后存儲(chǔ)下來 -
beresp.do_gunzip
: 從后端服務(wù)器收到的響應(yīng)報(bào)文炸宵,如果壓縮了辟躏,要不要解壓縮后再存下來 beresp.http.HEADER
beresp.proto
-
beresp.status
: 響應(yīng)狀態(tài)碼 -
beresp.response
: 響應(yīng)時(shí)的原因短語 -
beresp.ttl
: 響應(yīng)對(duì)象的剩余生存時(shí)長,單位為second -
beresp.backend.name
: 此響應(yīng)報(bào)文來源的backend主機(jī)名稱 beresp.backend.ip
beresp.backend.port
-
beresp.storage
: 緩存后端
-
-
5土全、緩存對(duì)象存入cache之后可用的變量捎琐,大多數(shù)為只讀
-
obj.response
: 服務(wù)端所返回的原因短語 -
obj.proto
: 響應(yīng)時(shí)使用的協(xié)議 obj.status
-
obj.ttl
: 指明當(dāng)前的對(duì)象緩存的還有多少時(shí)長 -
obj.hits
: 這個(gè)緩存對(duì)象已經(jīng)命中多少次(大約值) -
obj.http.HEADER
: 后端服務(wù)器的響應(yīng)首部
-
-
6会涎、決定對(duì)請(qǐng)求的健做hash計(jì)算時(shí)可用的變量
-
req.hash
: 把什么內(nèi)容做hash鍵,做查詢的健
-
-
7瑞凑、在為客戶端準(zhǔn)備響應(yīng)報(bào)文時(shí)可用的變量
-
resp.proto
: 指定使用什么協(xié)議來響應(yīng) resp.status
resp.response
resp.http.HEADER
-
vcl定義后端服務(wù)器主機(jī)
定義的后端主機(jī)需要在recv中調(diào)用末秃,必須會(huì)出錯(cuò)
-
1、定義格式:
backend NAME { .host= #后端backend server IP地址 .port= #后端backend server 端口 }
-
2拨黔、后端服務(wù)器需要vcl_recv中調(diào)用,示例如下
示例1: sub vcl_recv { ... if(req.url ~ "test.html") { set req.backend = NAME; } else { set req.backend = NAME1; } 示例2: sub vcl_recv { ... set req.backend = NAME; }
定義后端服務(wù)器集群
varnish中可以使用director
指令將一個(gè)或多個(gè)近似的后端主機(jī)定義成一個(gè)邏輯組蛔溃,并可以指定其調(diào)度方法(也叫挑選方法)來輪流將請(qǐng)求發(fā)送至后端backend主機(jī)上,不同的director可以使用同一個(gè)后端主機(jī)篱蝇,而某director也可以使用“匿名”后端主機(jī)(在director中直接定義)贺待,每個(gè)director都必須有其專用名,且在定義后必須在vcl中進(jìn)行調(diào)用零截,VCL中任何可以指定后端主機(jī)的位置均可按需將其替換為調(diào)用某已定義的director
-
1麸塞、定義方法
backend web1 { .host = "www.zhenping.me"; .port = "80"; } director webservers random { .retries = 5; { .backend = web1; .weight = 2; } { .backend = { .host = "www2.zhenping.me"; .port = "80"; } .weight = 3; } } 以上為兩種定義方法示例
-
2、調(diào)用方法
示例: sub vcl_recv { .... set req.backend = webservers; }
-
3涧衙、backend調(diào)度方法
-
round-robin
- 其是對(duì)資源對(duì)象的輪詢調(diào)度方法(當(dāng)訪問index1.html到后端server1,當(dāng)訪問index2.html到后端server2),其沒有參數(shù)
-
random
- 其是對(duì)后端backend server的隨機(jī)調(diào)度方法哪工,也是建議使用的方法,其支持以下參數(shù)
-
.weight = #
: 權(quán)重 -
.retires = #
: 來設(shè)定查找一個(gè)健康后端主機(jī)時(shí)的嘗試次數(shù)
-
varnish2.1.0之后弧哎,random挑選方法又多了兩種變化形式client和hash雁比,client類型的director使用client.identity作為挑選因子,這意味著client.identity相同的請(qǐng)求都將被發(fā)送至同一后端主機(jī)撤嫩,clinet.itdentity默認(rèn)為client.ip偎捎,但也可以在VCL中將其修改為所需要的標(biāo)識(shí)符,類似的序攘,hash類型的director使用hash數(shù)據(jù)作為挑選因子茴她,這意味著對(duì)同一個(gè)URL的請(qǐng)求將被發(fā)往同一個(gè)后端主機(jī),其常用于多級(jí)緩存的場(chǎng)景中程奠,然后丈牢,無論是client還是hash,當(dāng)其傾向于使用后端主機(jī)不可用時(shí)將會(huì)重新挑選新的后端主機(jī)
- 其是對(duì)后端backend server的隨機(jī)調(diào)度方法哪工,也是建議使用的方法,其支持以下參數(shù)
-
fallback
- 用于定義備用服務(wù)器瞄沙,其更多的是冗余的作用己沛,以下示例
director b3 fallback { { .backend = web1; } { .backend = web2; } { .backend = web3; } } 注意: 只有web1不可用才會(huì)使用到web2,web2和web1同時(shí)不可用時(shí),才會(huì)使用到web3,如果web2不可用距境,但web1可用泛粹,它也會(huì)使用web1
-
varnish檢測(cè)后端主機(jī)的健康狀態(tài)
varnish可以檢測(cè)后端主機(jī)的健康狀態(tài),在判定后端主機(jī)失效時(shí)能自動(dòng)將其從可用后端主機(jī)列表中移除肮疗,而一旦其重新變得可用還可以自動(dòng)將其設(shè)定為可用晶姊,為了避免誤判,varnish在探測(cè)后端主機(jī)的健康狀態(tài)發(fā)生轉(zhuǎn)變時(shí)(比如某次檢測(cè)時(shí)某后端主機(jī)突然成為不可用狀態(tài))伪货,通常需要連續(xù)執(zhí)行幾次探測(cè)均為新狀態(tài)才將其標(biāo)記為轉(zhuǎn)換后的狀態(tài)
每個(gè)后端服務(wù)器當(dāng)前探測(cè)的健康狀態(tài)探測(cè)方法通過.probe進(jìn)行設(shè)定们衙,其結(jié)果可由req.backend.healthy變量獲取钾怔,也可通過varnishlog中的backend_health查看或varnishadm的debug.health查看
-
.probe
中探測(cè)常用指令-
url
: 探測(cè)后端主機(jī)健康狀態(tài)時(shí)請(qǐng)求的URL,默認(rèn)為/ -
.request
: 探測(cè)后端主機(jī)健康狀態(tài)時(shí)所請(qǐng)求內(nèi)容的詳細(xì)格式蒙挑,定義后宗侦,它會(huì)替換.rul指定的探測(cè)方法,如下:
.request = "GET /.healthtest.html HTTP/1.1" "Host:www.zhenping.me" "Connection: close" #探測(cè)時(shí)關(guān)閉長連接
-
.window
: 設(shè)定在判定后端主機(jī)健康狀態(tài)時(shí)基于最近多少次的探測(cè)進(jìn)行忆蚀,默認(rèn)是8次 -
.threshold
: 在window中指定的次數(shù)中矾利,至少有多少次是成功的才判定后端主機(jī)是正常健康運(yùn)行,默認(rèn)是3次 -
.initial
: varnish啟動(dòng)時(shí)對(duì)后端主機(jī)至少需要多少次的成功探測(cè)馋袜,轉(zhuǎn)儲(chǔ)同.threshold
-
.expected_response
: 期望后端主機(jī)的響應(yīng)狀態(tài)碼男旗,默認(rèn)是200 -
interval
: 探測(cè)請(qǐng)求的發(fā)送周期,默認(rèn)是5秒 -
.timeout
: 每次探測(cè)請(qǐng)求的過期時(shí)長欣鳖,默認(rèn)為2秒
-
```
示例1:
backend web1 {
.host = "www.zhenping.me";
.probe = {
.url = "/.healthtest.html";
.interval = 1s;
.window = 5;
.threshold = 2;
}
}
示例2:
可以將probe的機(jī)制定義為一個(gè)代碼塊察皇,在backend中引用
probe PRO_NAME {
....;
}
backend NAME {
...;
.probe = PRO_NAME;
}
```
移除單個(gè)緩存對(duì)象
purge用于清理緩存中的某特定對(duì)象及其變種(variants),因此泽台,在有著明確要修剪的緩存對(duì)象時(shí)可以使用此種方式什荣。HTTP協(xié)議的PURGE方法可以實(shí)現(xiàn)purge功能,不過怀酷,其僅能用于vcl_hit和vcl_miss中稻爬,它會(huì)釋放內(nèi)存工作并移除指定緩存對(duì)象的所有Vary:-變種,并等待下一個(gè)針對(duì)此內(nèi)容的客戶端請(qǐng)求到達(dá)時(shí)刷新此內(nèi)容蜕依。另外桅锄,其一般要與return(restart)一起使用。下面是個(gè)在VCL中配置的示例
acl purgers {
"127.0.0.1";
"192.168.0.0"/24;
}
sub vcl_recv {
if (req.request != "GET" &&
req.request != "HEAD" &&
req.request != "PUT" &&
req.request != "POST" &&
req.request != "TRACE" &&
req.request != "OPTIONS" &&
req.request != "DELETE" &&
req.request != "PURGE") { #需要添加PURGE方法笔横,以不被送到PIPE引擎處理
/* Non-RFC2616 or CONNECT which is weird. */
return (pipe);
}
if (req.request != "GET" && req.request != "HEAD" && req.request != "PURGE") { #也需要添加PURGE方法不被送到PASS引擎,以確保PURGE方法可以到達(dá)HASH引擎
/* We only deal with GET and HEAD by default */
return (pass);
}
if (req.request == "PURGE") {
if (!client.ip ~ purgers) {
error 405 "Method not allowed";
}
return (lookup);
}
}
sub vcl_hit {
if (req.request == "PURGE") {
purge;
error 200 "Purged";
}
}
sub vcl_miss {
if (req.request == "PURGE") {
purge;
error 404 "Not in cache";
}
}
sub vcl_pass {
if (req.request == "PURGE") {
error 502 "PURGE on a passed object";
}
}
客戶端在發(fā)起HTTP請(qǐng)求時(shí)咐吼,只需要為所請(qǐng)求的URL使用PURGE方法即可吹缔,其命令使用方式如下:
# curl -I -X PURGE http://varniship/path/to/someurl
使用示例1
#drop any cookies sent to wordpress
sub vcl_recv {
if(!(req.url ~ “wp-(login|admin)”)) {
unset req.http.cookie;
}
}
使用示例2
sub vcl_recv {
if (req.http.host ~ “(?i)^(www.)?zhenping.me$”) {
set req.http.host = “www.zhenping.me”;
set req.backend = www;
} elseif (req.http.host ~ “(?i)^images.zhenping.me$”) {
set req.backend = images;
} else {
error 404 “Unknown virtual host”;
}
}
使用示例3
sub vcl_recv {
if (req.http.User-Agent ~ "iPad" || req.http.User-Agent ~ "iPhone" || req.http.User-Agent ~ "Android") {
set req.http.X-Device = "mobile";
} else {
set req.http.X-Device = "Desktop";
}
}
使用示例4(測(cè)試是否命中緩存)
sub vcl_deliver {
if (obj.hits > 0) {
set resp.http.X-Cache = "HIT";
} else {
set resp.http.X-Cache = "MISS";
}
return (deliver);
}
使用示例5(隱藏后端服務(wù)軟件版本)
sub vcl_deliver {
if (resp.http.Server) {
unset resp.http.Server; #出于安全考慮,需要將后端所使用的軟件名稱和版本隱藏起來
}
return (deliver);
}
生產(chǎn)環(huán)境實(shí)例
acl purge {
"localhost";
"127.0.0.1";
"10.1.0.0"/16;
"192.168.0.0"/16;
}
sub vcl_hash {
hash_data(req.url);
return (hash);
}
sub vcl_recv {
set req.backend = shopweb;
# set req.grace = 4h;
if (req.request == "PURGE") {
if (!client.ip ~ purge) {
error 405 "Not allowed.";
}
return(lookup);
}
if (req.request == "REPURGE") {
if (!client.ip ~ purge) {
error 405 "Not allowed.";
}
ban("req.http.host == " + req.http.host + " && req.url ~ " + req.url);
error 200 "Ban OK";
}
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") {
/* Non-RFC2616 or CONNECT which is weird. */
return (pipe);
}
if (req.request != "GET" && req.request != "HEAD") {
/* We only deal with GET and HEAD by default */
return (pass);
}
if (req.http.Authorization) {
/* Not cacheable by default */
return (pass);
}
if ( req.url == "/Heartbeat.html" ) {
return (pipe);
}
if ( req.url == "/" ) {
return (pipe);
}
if ( req.url == "/index.jsp" ) {
return (pipe);
}
if (req.http.Cookie ~ "dper=") {
return (pass);
}
if (req.http.Cookie ~ "sqltrace=") {
return (pass);
}
if (req.http.Cookie ~ "errortrace=") {
return (pass);
}
# if ( req.request == "GET" && req.url ~ "req.url ~ "^/shop/[0-9]+$" ) {
if ( req.url ~ "^/shop/[0-9]+$" || req.url ~ "^/shop/[0-9]?.*" ) {
return (lookup);
}
if ( req.url ~ "^/shop/(\d{1,})/editmember" || req.url ~ "^/shop/(\d{1,})/map" || req.url ~ "^/shop/(\d+)/dish-([^/]+)" ) {
return (lookup);
}
return (pass);
# return (lookup);
}
sub vcl_pipe {
return (pipe);
}
sub vcl_pass {
return (pass);
}
sub vcl_hit {
if (req.request == "PURGE") {
purge;
error 200 "Purged.";
}
return (deliver);
}
sub vcl_miss {
if (req.request == "PURGE") {
error 404 "Not in cache.";
}
# if (object needs ESI processing) {
# unset bereq.http.accept-encoding;
# }
return (fetch);
}
sub vcl_fetch {
set beresp.ttl = 3600s;
set beresp.http.expires = beresp.ttl;
#set beresp.grace = 4h;
# if (object needs ESI processing) {
# set beresp.do_esi = true;
# set beresp.do_gzip = true;
# }
if ( req.url ~ "^/shop/[0-9]+$" || req.url ~ "^/shop/[0-9]?.*" ) {
set beresp.ttl = 4h;
}
if ( req.url ~ "^/shop/(\d{1,})/editmember" || req.url ~ "^/shop/(\d{1,})/map" || req.url ~ "^/shop/(\d+)/dish-([^/]+)" ) {
set beresp.ttl = 24h;
}
if (beresp.status != 200){
return (hit_for_pass);
}
return (deliver);
}
sub vcl_deliver {
if (obj.hits > 0){
set resp.http.X-Cache = "HIT";
}
else {
set resp.http.X-Cache = "MISS";
}
set resp.http.X-Powered-By = "Cache on " + server.ip;
set resp.http.X-Age = resp.http.Age;
return (deliver);
}
sub vcl_error {
set obj.http.Content-Type = "text/html; charset=utf-8";
set obj.http.Retry-After = "5";
synthetic {""} + obj.status + " " + obj.response + {""};
return (deliver);
}
sub vcl_init {
return (ok);
}
sub vcl_fini {
return (ok);
}