一、Web緩存
1.web緩存
web緩存是可以自動保存常見文檔副本的HTTP設備。當Web請求抵達緩存時,如果本地有"已緩存的"副本,可以從本地的存儲設備而不是原始服務器設備中提取這個文檔。
通過key-value鍵值方式緩存,key中保存了URL路徑,value中保存了web內容,其均使用hash格式,算法保證能在海量數據中快速命中緩存內容
(1)緩存優點
1) 減少冗餘的數據傳輸,節省帶寬
2) 緩解網絡瓶頸問題,無需耕宇寬帶就能更快的加載頁面
3) 降低對原始服務器的要求,服務器可以更快的響應避免過載
4) 降低了距離時延
(2)相關術語
緩存命中 | cache hit,可以用已有的副本爲某些到達緩存的請求提供服務 |
緩存未命中 | cache miss,無緩存命中副本,被轉向後端原始服務器 |
HTTP再驗證 | revalidation,新鮮度檢測,緩存副本和原始服務器上的數據要不時的進行檢測是否爲最新 |
命中率 | cache hit rate,緩存提供服務的請求所佔的比例,命中率在0-1之間,通常用百分數描述 |
注意:
1) 命中率可以分爲:文檔命中率(從文檔個數進行衡量)、字節命中率(從內容大小進行衡量)
2) 緩存對象由其生命週期,需要定期清理
3)當緩存空間耗盡,會採用LRU(最近最少使用算法)進行覆蓋
4)不是所有的數據都能夠緩存,如首部Authorization、Cookie、Vary:accept-encoding= … ...
(3)緩存處理的步驟:
接收請求 -->解析請求(提取請求的URL及各種首部) --> 查詢緩存 --> 新鮮度檢測 --> 創建響應報文--> 發送響應 --> 記錄日誌
2.HTTP首部緩存控制機制
Cache-Control: no-store禁止緩存對響應進行復制
Cache-Control: no-cache可以存儲在本地緩存區域中,只是在於原始服務器進行新鮮度在驗證之前緩存不能提供給客戶端使用
Cache-Control:must-revalidate嚴格遵循過期信息,在實現沒和服務器再驗證不提供對象舊的副本。新鮮度檢測失敗返回504
Cache-Control: max-age讀秒,僅適用於公共緩存
Expries日期首部到響應中,指明具體時間,不推薦使用
不添加過期信息,讓緩存確定自己的過期日期
(1)新鮮度檢測機制
在HTTP協議的報文首部會對web作出首部定義,通過首部的報文對比來做新鮮度檢測。HTTP/1.0 Expires定義的是絕對時間,當達到指定的日期時間的時候,緩存失效;HTTP/1.1 Cache-Control:max-age=#定義的是相對時間,記秒,已解決HTTP/1.0協議在解決全球性數據緩存檢測時各地區時差產生的問題
實例:
HTTP/1.0 Expires:Thu, 04 Jun 2015 23:38:18 GMT
HTTP/1.1Cache-Control:max-age=600
(2)有效性再驗正:revalidate
如果原始內容未改變,則僅響應首部(不附帶body部分),響應碼304(Not Modified)
如果原始內容發生改變,則正常響應,響應碼200;
如果原始內容消失,則響應404,此時緩存中的cacheobject也應該被刪除;
說明:
相關條件式請求首部:If-Modified-Since、If-Unmodified-Since、If-Match、If-None-Match:
If-Modified-Sicce:<date> | 如果從指定日期之後文檔被修改了,就執行請求的方法。可以與Last-Modified(最後被修改)服務器響應首部配合使用,只有在內容被修改後與已緩存版本有所不同時候採取獲取內容 |
If-None_Match:<tags> | 標籤,如果一緩存標籤與服務器文檔中的標籤不同,If-None_Match首部就會執行請求的方法 |
HTTP緩存報文實例:
Cache-Control = "Cache-Control" ":"1#cache-directive
cache-directive = cache-request-directive
| cache-response-directive
cache-request-directive =
"no-cache"
| "no-store" (backup)
| "max-age" "="delta-seconds
| "max-stale" ["=" delta-seconds ]
| "min-fresh" "="delta-seconds
| "no-transform"
| "only-if-cached"
| cache-extension
cache-response-directive =
"public"
| "private" [ "="<"> 1#field-name <"> ]
| "no-cache" [ "="<"> 1#field-name <"> ]
| "no-store"
| "no-transform"
| "must-revalidate"
| "proxy-revalidate"
| "max-age" "="delta-seconds
| "s-maxage" "="delta-seconds
| cache-extension
3.常見的緩存服務開源解決方案
軟件 | 存儲模式 | 性能 | 配置複雜度 | purge效率 | 共享存儲 |
squid | 硬盤 | 較高 | 簡單 | 低 | 可以並聯,但是配置複雜 |
varnish | 硬盤/內存 | 高 | 比較簡單 | 低 | 不能 |
說明:
Squid,很古老的反向代理軟件,擁有傳統代理、身份驗證、流量管理等高級功能,但是配置太複雜。優勢在於完整的龐大的cache技術資料。Squid在大規模負載均衡場景下很穩定
Varnish是新興的一個軟件,設計簡單,基於內存緩存,重啓後數據將消失。
(1)varnish對比squid的優點
1)varnish穩定性很高,兩者在完成相同負荷的工作時,squid服務器發生故障的機率要高於varnish,因爲squid要經常重啓
2)varnish訪問速度更快,其採用了"Visual PageCache"技術,所有緩存數據都直接從內存中讀取,而squid是從硬盤讀取,因而varnish在訪問速度方面會更快
3)varnish可支持更多併發連接,因爲varnish的TCP連接釋放要比squid快,因而在高併發連接情況下可以支持更多TCP連接
4)varnish可以通過管理端口,使用正則表達式批量的清除部分緩存,而squid是做不到的。
5)squid屬於單進程使用單核CPU,但Varnish是通過fork形式打開多進程來做處理,所以是合理的使用所有核來處理相應的請求
(2)varnish對比squid的缺點
1)varnish進程一旦Hang、Crash或者重啓,緩存數據都會從內存中完全釋放,此時所有請求都會發送到後端服務器,在高併發情況下,會給後端服務器造成很大的壓力
2)在varnish使用中,如果單個vrl的請求通過HA/F5,每次請求不同的varnish服務器時,被請求的varnish服務器都會被穿透到後端,而同樣的請求會在多臺服務器上緩存,也會造成varnish的緩存資源浪費,也會造成性能下降
二、Varnish緩存概述
1.varnish程序架構
varnish是高性能開源反向代理服務器,http加速器。官方網址:www.varnish-cache.org。版本:v2,v3,v4
架構圖:
主要運行兩個進程:Management進程和Child(Cache)進程
(1) Management管理進程
主要實現應用新的配置、編譯VCL、監控varnish、初始化varnish以及提供一個命令行接口等。Management進程會定期探測Child進程。若在指定時長未得到Child進程迴應,會重啓此Child進程。
配置文件是VCL編程語言格式,定義緩存工作功能的,VCL編譯器需要調用C編譯器編譯後成varnish能加載的格式才能使用
(2) Child(Cache)進程
Child進程包含多種類型的線程,主要用於用戶請求管理和緩存管理
Acceptor:接收器,接收新的連接請求並響應;
Worker:處理用戶請求
Expiry:從緩存中清理過期緩存內容;
cache-worker線程
cache-main線程:此線程只有一個,用於啓動caceh;
ban luker:緩存清理線程
acceptor:接收線程
epoll:線程池管理器
expire:清理過期緩存
注意:
Varnish依賴“工作區(workspace)”以降低線程在申請或修改內存時出現競爭的可能性。在varnish內部有多種不同的工作區,其中最關鍵的當屬用於管理會話數據的session工作區。
(3) VCL和C編譯器
VCL編寫的緩存策略保存至.vcl文件中,其需要編譯成二進制的格式後才能由varnish調用。
VCL策略在啓用前,會由management進程將其轉換爲C代碼,而後再由gcc編譯器將C代碼編譯成二進制程序。編譯完成後,management負責將其連接至varnish實例,即child進程。
(4) 調用接口
CLIInterface(命令行接口)、telent Interface(文本協議不安全)、web Interface(商業)
(5) varnish log file日誌
1) 共享內存日誌(shared memory log):日誌保存在內存中的共享空間,大小一般爲90M,其分爲兩部分,前一部分爲計數器,後半部分爲客戶端請求的數據。如果某線程需要記錄信息,其僅需要持有一個鎖,而後向共享內存中的某內存區域寫入數據,再釋放持有的鎖
2) varnish log在內存中實現日誌保存,默認不能長久保存,會循環保存
3) varnish提供多個工具如varnishlog、varnishncsa或varnishstat等來分析共享內存日誌中的信息並以指定的方式進行顯示。
2.VCL:配置緩存策略工具
Varnish Configuration Language (VCL):
(1)基於“域”(domain specific)的簡單編程語言
(2)支持有限的算術運算和邏輯運算操作
(3)允許使用正則表達式進行字符串匹配
(4)允許用戶使用set自定義變量
(5)支持if判斷語句
(6)支持內置的函數和變量等。
3.varnish存儲類型
(1)file:單個文件;不支持持久機制
使用特定的文件存儲全部的緩存數據,並通過操作系統的mmap()系統調用將整個緩存文件映射至內存區域
(2)malloc:內存方式
使用malloc()庫調用在varnish啓動時向操作系統申請指定大小的內存空間以存儲緩存對象;
(3)persistent(experimental):基於文件的持久存儲
與file的功能相同,但可以持久存儲數據(即重啓varnish數據時不會被清除);仍處於測試期;
注意:
1) file存儲方法在varnish停止或重啓時會清除數據:varnish無法追蹤某緩存對象是否存入了緩存文件
2) persistent僅適用於有巨大緩存空間的場景,目前尚無法有效處理要緩存對象總體大小超出緩存空間的情況,
3) 建議在內存空間足以存儲所有的緩存對象時使用malloc的方法,反之,file存儲將有着更好的性能的表現。
4) 對於100萬個緩存對象的場景來說,其使用的緩存空間將超出指定大小1G左右。另外,爲了保存數據結構等,varnish自身也會佔去不小的內存空間。
三、安裝啓動varnish服務
1.安裝相關程序包
[root@localhost ~]#yum install -y gcc varnish-3.0.4-1.el6.x86_64.rpm varnish-docs-3.0.4-1.el6.x86_64.rpmvarnish-libs-3.0.4-1.el6.x86_64.rpm varnish-libs-devel-3.0.4-1.el6.x86_64.rpm
說明:
1) gcc編輯器是varnish編譯val語言所需的依賴組件
2) varnish提供程序的主包、varnish-libs提供程序相關的庫,必須安裝
3) varnish-libs-devel提供二次開發所依賴的庫文件、varnish-docs提供varnish幫助文檔,可以視情況安裝
2.配置varnish啓動時腳本配置文件/etc/sysconfig/varnish
[root@localhost ~]# vim /etc/sysconfig/varnish
NFILES=131072 | 打開的最大文件數 |
MEMLOCK=82000 | 鎖定共享內存大小 |
NPROCS="unlimited" | 最大線程數,無限制 |
RELOAD_VCL=1 | 自動重新裝載緩存策略,1表示腳本啓動自動重新裝載緩存策略配置文件 |
VARNISH_VCL_CONF=/etc/varnish/default.vcl | 默認的緩存策略配置文件 |
VARNISH_LISTEN_PORT=6081 | 默認監聽端口,一般改爲80端口 |
VARNISH_ADMIN_LISTEN_ADDRESS=127.0.0.1 | 管理員management進程管理地址 |
VARNISH_ADMIN_LISTEN_PORT=6082 | 管理員management進程管理端口 |
VARNISH_SECRET_FILE=/etc/varnish/secret | 啓動裝載的密鑰文件位置,域共享密鑰 |
VARNISH_MIN_THREADS=50 | 最少線程,啓動時最騷啓動的空閒進程 |
VARNISH_MAX_THREADS=1000 | 最大啓動進程,最多不能超過5000個併發 |
VARNISH_THREAD_TIMEOUT=120 | 線程超時時長 |
VARNISH_STORAGE_FILE=/var/lib/varnish/varnish_storage.bin | 緩存文件存放文件(二進制文件) |
VARNISH_STORAGE_SIZE=1G | 緩存大小 |
VARNISH_STORAGE="file,${VARNISH_STORAGE_FILE},${VARNISH_STORAGE_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}" |
注意:
以下配置必須修改:VARNISH_LISTEN_PORT=80,一般改爲80端口,響應http請求,做爲web緩存
VARNISH_STORAGE,一般在大規模圖片緩存時候可以爲file,但時硬件上最好是固態硬盤
3.啓動服務
[root@localhost~]# service varnish start
四、varnish命令行管理工具
1.varnishadm:登錄命令行終端
命令語法:varnishadm[-t timeout] [-S secret_file] [-T address:port] [-n name] [command [...]]
[root@localhost~]# varnishadm -S /etc/varnish/secret -T 127.0.0.1:6082
varnish> help
vcl.list | 查看配置文件 |
vcl.load | 重新編譯加載vcl配置,需要指明自定義版本名稱,和需要編譯的文件default.vcl位置(相對) |
vcl.use | 使用編譯的配置文件,指明自定義版本名稱 |
vcl.show | 顯示指定的配置文件詳細信息,指明自定義版本名稱 |
vcl.discard | 手動清理版本,否則舊版本配置信息會在varnish重啓後丟棄 |
2.varnish的param(參數)查看及設置
param.show[-l] [param]
param.set[param] [value]
varnish通過線程池模型定義其最大併發連接數:
thread_pools | 線程池個數;默認爲2; |
thread_pool_max | 單個線程池內允許啓動的最多線程個數; |
thread_pool_min | 啓動時要啓動的線程數; |
thread_pool_purge_delay | 清理線程的時間長度,最小不能100毫秒 |
thread_pool_stack | 工作線程棧,默認爲-1,unlimited,不做限制 |
thread_pool_timeout | 超時時長,多於thread_pool_min的線程空閒此參數指定的時長後即被purge |
thread_pool_wokerspace | 工作空間,默認使用內存65536字節 |
3.varnishtop: 內存日誌區域查看工具
-Iregexp: 僅顯示被模式匹配到的條目
-Xregexp:僅顯示不被模式匹配到的條目
-C:忽略字符大小寫;
-d:顯示已有日誌;
varnish>varnishtopUser-Agent
顯示結果實例:
RxHeader User-Agent:Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko)Chrome/43.0.2357.81 Safari/537.36
說明:
RxHeader:稱爲tag,基於tag過濾,可使用-i或-x選項;
User-Agent起始的內容:稱爲日誌信息,可使用-I或-X選項進行過濾;
4.varnishstat:varnish數據統計工具
-f field, field, …:指明顯示的字段
-l:列出所有可用字段
-x:xml輸出格式
-j:json輸出格式
五、VCL狀態引擎制定緩存策略
1.varnish狀態引擎
varnish內部有幾個所謂的狀態(state),在這些狀態上可以附加通過VCL定義的策略以完成相應的緩存處理機制,因此VCL也經常被稱作“域專用”語言或狀態引擎,“域專用”指的是有些數據僅出現於特定的狀態中。
狀態之間具有相關性,但彼此間互相隔離,每個引擎使用return(x)來退出當前狀態並指示varnish進入下一個狀態。
(1)狀態引擎分類
vcl_recv、vcl_hash、vcl_hit、vcl_miss、vcl_fetch、vcl_deliver、vcl_pipe、vcl_pass、vcl_error
vcl_init | 在裝載vcl,用其處理任何請求之前 |
vcl_recv | 請求被接入,但在其被分析、處理完成之前;是否服務此請求、如何服務、使用哪個後端主機爲其服務 |
vcl_pipe | 方法不理解 |
vcl_pass | 不查詢緩存 |
vcl_hash | 查找緩存,調用內置hash_data()函數,決定對什麼內容hash計算,如後端存在虛擬主機,但用ip訪問不能命中 |
vcl_hit | 命中 |
vcl_miss | 未命中 |
vcl_fetch | 在後端服務器取內容,其能讓可緩存或不緩存。從後端主機收到響應報文之前會被調用,返回的值可以爲deliver、error code [reason]、hit_for_pass、restart |
vcl_deliver | 傳送 |
vcl_error | 錯誤處理機制 |
(2)狀態引擎工作流程(v3):
varnish先對HTTP請求本身進本分析,再由vcl_recv方法完成決策(是否在緩存中查詢請求額資源)
vcl_recv--> vcl_hash --> vcl_hit --> vcl_deliver
vcl_recv--> vcl_hash --> vcl_miss --> vcl_fetch --> vcl_deliver
vcl_recv--> vcl_pass --> vcl_fetch --> vcl_deliver
vcl_recv--> vcl_pipe
(3)vcl_recv引擎
vcl_recv是在Varnish完成對請求報文的解碼爲基本數據結構後第一個要執行的子例程,它通常有四個主要用途:
1)修改客戶端數據以減少緩存對象差異性;比如刪除URL中的www.等字符;
2)基於客戶端數據選用緩存策略;比如僅緩存特定的URL請求、不緩存POST請求等;
3)爲某web應用程序執行URL重寫規則;
4)挑選合適的後端Web服務器;
可以通過return()向Varnish返回的指示操作:
pass:繞過緩存,即不從緩存中查詢內容或不將內容存儲至緩存中;
pipe:不對客戶端進行檢查或做出任何操作,而是在客戶端與後端服務器之間建立專用“管道”,並直接將數據在二者之間進行傳送;此時,keep-alive連接中後續傳送的數據也都將通過此管道進行直接傳送,並不會出現在任何日誌中;
lookup:在緩存中查找用戶請求的對象,如果緩存中沒有其請求的對象,後續操作很可能會將其請求的對象進行緩存;
error:由Varnish自己合成一個響應報文,一般是響應一個錯誤類信息、重定向類信息或負載均衡器返回的後端web服務器健康狀態檢查類信息;
注意:
1) Varnish默認的vcl_recv專門設計用來實現安全的緩存策略,它主要完成兩種功能:
a)僅處理可以識別的HTTP方法,並且只緩存GET和HEAD方法;
b)不緩存任何用戶特有的數據;
2) 一般自定義vcl_recv中不要使用return()終止語句,而是再由默認vcl_recv進行處理,並由其做出相應的處理決策。
#sub vcl_recv {
# 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 isweird. */
# return (pipe);
# }
# if (req.request != "GET"&& req.request != "HEAD") {
# /* We only deal with GET and HEAD bydefault */
# return (pass);
# }
# if (req.http.Authorization ||req.http.Cookie) {
# /* Not cacheable by default */
# return (pass);
# }
# return (lookup);
#}
(3)vcl_fetch引擎
vcl_fetch是根據服務器端的響應作出緩存決策。在任何VCL狀態引擎中返回pass操作都將由vcl_fetch進行後續處理。默認的vcl_fetch放棄了緩存任何使用了Set-Cookie首部的響應。通過return()返回給varnish的操作指示有:
1)deliver:緩存此對象,並將其發送給客戶端(經由vcl_deliver);
2)hit_for_pass:不緩存此對象,但可以導致後續對此對象的請求直接送達到vcl_pass進行處理;
3)restart:重啓整個VCL,並增加重啓計數;超出max_restarts限定的最大重啓次數後將會返回錯誤信息;
4)errorcode [reason]:返回指定的錯誤代碼給客戶端並丟棄此請求;
2.VCL編程語言語法
(1) //, #, /* */用於註釋;會被編譯器忽略;
(2) sub $name定義函數,不接受參數;VCL內部的數據傳遞只能隱藏在HTTP首部內部進行
(3) 不支持循環語句;
(4)有衆多內置的變量,變量的可調用位置與state engine有密切相關性;
(5)支持終止語句,return(action);沒有返回值;
(6) "域"專用語言,只能用在特定的域上;
(7) 支持衆多操作符:=(賦值)、==(等值比較)、~(模式匹配)、!(取反)、&&(邏輯與)、||(邏輯或)
(8) 一般爲條件判斷語句:單分支、雙分支、多分支
3.varnish內置函數
(1)regsub(str,regex,sub)
regsuball(str,regex,sub):這兩個用於基於正則表達式搜索指定的字符串並將其替換爲指定的字符串;但regsuball()可以將str中能夠被regex匹配到的字符串統統替換爲sub,regsub()只替換一次;
(2)ban(expression):
ban_url(regex):Bans所有其URL能夠由regex匹配的緩存對象;
(3)purge:
從緩存中挑選出某對象以及其相關變種一併刪除,這可以通過HTTP協議的PURGE方法完成;
(4)hash_data(str):
return():當某VCL域運行結束時將控制權返回給Varnish,並指示Varnish如何進行後續的動作;其可以返回的指令包括:lookup、pass、pipe、hit_for_pass、fetch、deliver和hash等;但某特定域可能僅能返回某些特定的指令
return(restart)重新運行整個VCL,即重新從vcl_recv開始進行處理;每一次重啓都會增加req.restarts變量中的值,而max_restarts參數則用於限定最大重啓次數。
4.常用變量
(1)在任何狀態引擎中均可
.host後端主機的主機名或者IP
.port後端主機的服務名或者端口號
(2)用於處理請求階段
client.ip客戶端ip
server.hostname緩存服務器主機名
server.ip緩存服務器ip
server.port緩存服務器端口
req.request:請求方法
req.url:請求的URL
req.proto:請求的HTTP協議版本
req.backend:指定用於服務此次請求的後端主機
req.backend.healthy:後端主機的健康狀態
req.http.HEADER:調用request報文中http協議的指定的HEADER首部
req.can_gzip:客戶端是否能結束gzip壓縮格式響應內容
req.restarts:此請求被重啓的次數
req.grace:寬限期響應
(3)varnish向backend主機發起請求前可用的變量
bereq.request:後端主機請求方法
bereq.url:後端主機請求路徑
bereq.proto:後端主機請求http協議
bereq.http.HEADER:後端主機HTTP首部
bereq.connect.timeout:等待後端建立連接的超時時長
(4)backend主機的響應報文到達本主機(varnish)後,將其放置在cache中之前可用的變量
beresp.do_stream:流式響應(接收一個響應一個)
beresp.do_gzip:是否壓縮後存儲
beresp.do_gunzip:是否先解壓縮再存儲
beresp.http.HEADER:後端主機響應http首部
beresp.proto:後端主機響應http協議
beresp.status:後端主機響應狀態碼
beresp.response:響應的原因短語
beresp.ttl:響應對象剩餘的響應時長,單位爲秒
beresp.backend.name指明此響應報文來源的後端主機的名稱
beresp.backend.ip指明此響應報文來源的後端主機的ip地址
beresp.backend.port指明此響應報文來源的後端主機的端口號
beresp.storage強制varnish存儲在指定的緩存後端
(5)緩存對象(從後端主機取出內容)存入cache之後可用變量
obj.proto:緩存對象響應時的協議
obj.status:緩存對象響應時的狀態碼
obj.response:緩存對象響應時的原因短語
obj.ttl:緩存對象響應時的生存時長
obj.hits:緩存對象響應時的命中次數
obj.http.HEADER:緩存對象響應時的的http首部
(6)在決定對請求的鍵做hash計算時可用變量
req.hash:指明將什麼鍵當成hash緩存的鍵
(7)在爲客戶端準備響應報文時可用的變量
resp.proto響應的協議
resp.status響應的狀態碼
resp.response響應的響應原因短語
resp.http.HEADER響應的
變量的可用位置:
官方文檔:https://www.varnish-cache.org/docs/4.0/reference/vcl.html#varnish-configuration-language
5.使用案例概述
(1)變量賦值:set name=value
實例:定義在vcl_deliver中,向響應給客戶端的報文添加一個自定義首部X-Cache;
if (obj.hits>0) {
set resp.http.X-Cache ="HIT";
} else {
set resp.http.X-Cahce ="MISS";
}
(2)支持虛擬主機:
if(req.http.host == "www.magedu.com"){
}
(3)強制對某資源的請求,不檢查緩存;
實例:對URL以/admin和/login結尾不做緩存:
if(req.url ~ "(?i)^/login" || req.url ~ "(?i)^/admin") {
return(pass);
}
(4)對特定類型的資源取消其私有的cookie標識,並強行設定其可以varnish緩存的時長:
vcl_backend_response
if(beresp.http.cache-control !~ "s-maxage") {
if(bereq.url ~ "(?i)\.jpg$") {
setberesp.ttl = 3600s;
unsetberesp.http.Set-Cookie;
}
if(bereq.url ~ "(?i)\.css$") {
setberesp.ttl = 600s;
unsetberesp.http.Set-Cookie;
}
}
官方配置示例:https://www.varnish-cache.org/trac/wiki/VCLExamples
六、varnish後端主機健康監測和負載均衡
1.backend server的定義
backend name {
.attribute ="value";
}
.host | 後端主機的IP; |
.port | 後端主機監聽的PORT; |
.probe | 對後端做健康狀態檢測; |
.max_connections | 並連接最大數量; |
2.後端主機的健康狀態檢測方式
probe name {
.attribute= "value";
}
.url: | 判定BE健康與否要請求的url; |
.expected_response | 期望響應狀態碼;默認爲200; |
實例一:
backendwebsrv1 {
.host = "172.16.100.68";
.port = "80";
.probe = {
.url = "/test1.html";
}
}
backendwebsrv2 {
.host = "172.16.100.69";
.port = "80";
.probe = {
.url = "/test1.html";
}
}
subvcl_recv {
if (req.url ~"(?i)\.(jpg|png|gif)$") {
set req.backend_hint = websrv1;
} else {
set req.backend_hint = websrv2;
}
}
實例二:
importdirectors;
subvcl_init {
new mycluster = directors.round_robin();
mycluster.add_backend(websrv1);
mycluster.add_backend(websrv2);
}
vcl_recv{
setreq.backend_hint = mycluster.backend();
}
負載均衡算法:fallback,random, round_robin, hash
七、實際案例
1.移除單個緩存對象
purge用於清理緩存中的某特定對象及其變種(variants),因此,在有着明確要修剪的緩存對象時可以使用此種方式。HTTP協議的PURGE方法可以實現purge功能,不過,其僅能用於vcl_hit和vcl_miss中,它會釋放內存工作並移除指定緩存對象的所有Vary:-變種,並等待下一個針對此內容的客戶端請求到達時刷新此內容。另外,其一般要與return(restart)一起使用。下面是個在VCL中配置的示例。
aclpurgers {
"127.0.0.1";
"192.168.0.0"/24;
}
subvcl_recv {
if(req.request == "PURGE") {
if(!client.ip ~ purgers) {
error405 "Method not allowed";
}
return(lookup);
}
}
subvcl_hit {
if(req.request == "PURGE") {
purge;
error200 "Purged";
}
}
subvcl_miss {
if(req.request == "PURGE") {
purge;
error404 "Not in cache";
}
}
subvcl_pass {
if(req.request == "PURGE") {
error502 "PURGE on a passed object";
}
}
客戶端在發起HTTP請求時,只需要爲所請求的URL使用PURGE方法即可,其命令使用方式如下:
#curl -I -X PURGE http://varniship/path/to/someurl
2.生產環境案例實例
aclpurge {
"localhost";
"127.0.0.1";
"10.1.0.0"/16;
"192.168.0.0"/16;
}
subvcl_hash {
hash_data(req.url);
return (hash);
}
subvcl_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 bydefault */
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);
}
subvcl_pipe {
return (pipe);
}
subvcl_pass {
return (pass);
}
subvcl_hit {
if (req.request == "PURGE") {
purge;
error 200 "Purged.";
}
return (deliver);
}
subvcl_miss {
if (req.request == "PURGE") {
error 404 "Not in cache.";
}
# if (object needs ESI processing) {
# unset bereq.http.accept-encoding;
# }
return (fetch);
}
subvcl_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);
}
subvcl_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);
}
subvcl_error {
set obj.http.Content-Type = "text/html;charset=utf-8";
set obj.http.Retry-After = "5";
synthetic {""} + obj.status +" " + obj.response + {""};
return (deliver);
}
subvcl_init {
return (ok);
}
subvcl_fini {
return (ok);
}