varnish詳解

        Varnish是一款高性能的開源HTTP緩存加速器,從下圖(網站拓撲結構視圖)不難看出,緩存對於現代互聯網的重要意義。

wKioL1Vy3eGRKug7AATFkGNpu3Q633.jpg

基礎理論

緩存存在的基礎

程序具有局部性

時間局部性

過去訪問過的數據在一段時間內有可能被再次訪問

空間局部性

過去被訪問的某數據周圍的其他數據有可能也被訪問

緩存得以生效就是因爲程序的局部性

緩存的存儲方式

緩存以key-value形式存儲

key:訪問路徑,url,經過hash計算後存儲;

value:web content

緩存主要緩存的是熱點數據

緩存命中率

hit/(hit+miss)

衡量標準

文檔命中率:以命中文檔的個數進行衡量;

字節命中率:以命中內容的大小進行衡量;

注意事項

1、緩存對象有生命週期,所以需定期清理;

2、當緩存耗盡時,可基於LRU(最近最少使用)算法,實時清理一部分緩存,以騰出空間存放新的緩存;

3、緩存需注意私密性,有些內容不可緩存,如用戶的私有數據、報文首部中包含“Authorization,Cookie, Vary: accept-encoding=”等信息的;

緩存處理步驟

接收請求 --> 解析請求(提取請求的url及各種首部)--> 查詢緩存 --> 新鮮度檢測 --> 構建響應報文--> 發送響應 --> 記錄日誌

新鮮度檢測機制

過期機制

HTTP/1.0,在報文首部Expires,例如

“Expires:Thu, 04 Jun 2015 23:38:18 GMT”,使用的是絕對時間,規定在此時間之後的緩存都是不新鮮的,考慮到時區問題,此種設定有天然缺陷;

HTTP/1.1,Cache-Controlmax-age,例如

“Cache-Control:max-age=600”,使用的是相對時間,規定緩存的最大存活時長;

服務器有效性再驗證

revalidate

如果原始內容未改變,則僅響應報文首部(不附帶body部分),響應碼304(Not Modified);

如果原始內容改變,則正常響應,響應碼200;

如果原始內容消失,則響應404,此時緩存中的cache object也相應被刪除;

條件式請求首部

If-Modified-Since:

If-Unmodified-Since:

基於請求內容的時間戳作驗證;

缺陷:若後端服務器內容改變頻率爲“毫秒”級,時間戳的最小單位爲“秒”,此種驗證會導致使用過期緩存;

If-Match:

If-None-Match:

對每個生成的內容作一個“Etag”標記,基於此特徵碼作驗證;

常見緩存服務器開源解決方案

varnish,squid

緩存首先必須是一個反向代理

varnish基礎

https://www.varnish-cache.org/

varnish工作架構圖

wKiom1Vy3fzA7Yd8AAFVIBOjgxA386.jpg

Management管理進程

編譯VCL並應用新配置、監控varnish、初始化varnish並提供命令行接口CLI;

Child/Cache線程

Acceptor:用於接收並連接請求;

Worker:用於處理並響應用戶請求;

Object expiry:從緩存中清理過期的cache object;

cache-main:此線程只有一個,用於啓動cache;

banluker:清理指定緩存;

expire:清理過期緩存

epoll:線程池管理器;

varnish定義其最大併發連接數是通過線程池模型實現的:

thread_pools:線程池個數,默認爲2;

thread_pool_max:單線程池內允許啓動的最大線程個數;

thread_pool_min:單線程池內允許啓動的最小線程個數;

thread_pool_timeout:多於thread_pool_min的線程空閒此參數指定的時長後即被purge;

varnish的param查看及設置方法

使用“varnishadm”命令後使用如下命令實現:

param.show[-l] [param]

param.set[param] [value]

Log file

varnish的日誌,爲保持其高性能,保持在一段共享內存空間中--Shared Memory Log,共享內存日誌大小一般爲90M,分爲兩部分:前一部分爲計數器,後一部分爲客戶請求相關的數據,可以計算緩存命中率、分析請求首部、記錄響應首部等;

vcl:Varnish Configuration Language

緩存策略配置工具;

基於“域”的簡單編程語言,意爲其所有代碼都是寫在各“{}”中,且作用範圍僅爲對應的“{}”,這些“域”稱爲varnish的狀態引擎;

varnish定義其最大併發連接數:線程池模型:

thread_pools:線程池個數;默認爲2;

thread_pool_max:單線程池內允許啓動的最多線程個數;

thread_pool_min

thread_pool_timeout:多於thread_pool_min的線程空閒此參數指定的時長後即被purge;

vcl處理流程圖

        wKioL1Vy3-ijNoqAAAF5Dwd-jl4714.jpg

        wKiom1Vy3lOBpplQAAE9pHjVWoA743.jpg


state engine

vcl配置的緩存策略在這些stateengine上發揮作用;state engine之間有相關性,上級engine通過return指明下級engine;varnish處理請求前要先分析該請求的http首部;

vcl_init:

在裝載vcl,用其處理任何請求之前;

vcl_recv

請求被接入,但在其被分析、處理完成之前;

是否響應此請求、如何響應、使用哪個後端主機響應;

vcl_pipe

vcl_hash

vcl_hit

vcl_miss

vcl_pass

vcl_fetch

從backend主機收到響應報文之前被調用;可return的值:deliver、errorcode [reason]、restart;

vcl_deliver

vcl_error

配置文件

lftp172.16.0.1:/pub/Sources/6.x86_64/varnish> mget *.rpm(源碼包位置)

varnish-3.0.6-1.el6.x86_64.rpm、varnish-libs-3.0.6-1.el6.x86_64.rpm、varnish-docs-3.0.6-1.el6.x86_64.rpm(此包非必須,僅用於提供各種說明文檔)、varnish-libs-devel-3.0.6-1.el6.x86_64.rpm(若不做二次開發,此包無需安裝)

/etc/varnish/default.vcl

配置語法

(1) //,#,註釋單行,/*comment*/註釋多行;

(2) sub $NAME用於定義函數,但函數不接受參數;

(3) 不支持循環;

(4) 支持衆多內置變量;

(5) 支持使用終止語句,但沒有返回值;

(6) “域”專用語言,即所寫代碼只能應用在特定的域上;

(7) 支持衆多操作符:=,==,~,!,&&,||;

varnish命令行工具

varnishadm

varnishadm [-t  timeout]  [-S secret_file]  [-T address:port] [-nname] [command [...]]

vcl.load:重載

vcl.use:使用

vcl.show:顯示

vcl.discard:刪除

varnishd

 varnishd [-a address[:port]] [-b host[:port]][-d] [-F] [-f config]

-a:指明監聽的地址和端口,默認端口爲6081;

-b:指明後端主機和端口;

-d:打開debug模式;

-F:運行於前臺;

-f:指明配置文件;

-l:指明用於保存日誌文件的內存空間大小;

-s[name=]type[,options]:指明使用的存儲後端;

varnishtop

內存日誌區域查看工具

-I regex:僅顯示被模式匹配到的項目;

varnish支持的後端存儲機制

-s type

malloc[size]--在內存中保存緩存

file[path[size[granularity]]]--在磁盤中使用單個文件保存所有緩存

persistent,path,size--持久保存,但目前仍處於測試階段

注意:varnish進程一旦重啓,前兩種存儲機制保存的所有緩存對象將被一律清空;對緩存來講,預熱十分重要,所謂預熱即找出熱點數據,保存在緩存中;

varnish配置

1、啓動varnish前要先定義其腳本配置文件

配置打開資源限制段

NFILES=131072

多能打開的最大文件數,varnish會自動調整該值;

MEMLOCK=82000

所能使用的內存空間,varnish會自動調整該值;

NPROCS="unlimited"

單個用戶所能運行的最大線程數;

RELOAD_VCL=1

定義varnish是否會自動重新裝載其緩存策略配置文件;“=1”表示在我們使用腳本重啓varnish時,其會自動重載vcl配置文件;

配置工作特性段

Alternative 1, Minimal configuration, no VCL

最小化配置,無緩存策略

Alternative 2, Configuration with VCL

使用vcl實現後續緩存配置

Alternative 3, Advanced configuration

高級配置,定義各種參數,默認使用此種配置方法

Alternative 4, Do It Yourself. See varnishd(1) formore information.

有如上4種可用配置選擇,使用其中一種進行配置即可

採用Alternative 3的詳細配置選項

VARNISH_VCL_CONF=/etc/varnish/default.vcl

緩存策略的默認讀取文件;

VARNISH_LISTEN_PORT=80

因爲varnish首先是作爲web服務器的反向代理工作的,所以一般應監聽於80端口;

VARNISH_ADMIN_LISTEN_ADDRESS=127.0.0.1

VARNISH_ADMIN_LISTEN_PORT=6082

client interface聯繫varnish的配置接口,一般此配置接口不應該允許遠程打開,所以監聽地址爲本機;

VARNISH_SECRET_FILE=/etc/varnish/secret

使用client interface接口聯繫varnish時使用的密鑰文件;

VARNISH_MAX_THREADS=3000

啓用的最大線程數,因爲varnish的併發能力有限,所以一般不要超過5000,否則可能導致不穩定;

#VARNISH_STORAGE="file,${VARNISH_STORAGE_FILE},${VARNISH_STORAGE_SIZE}"

VARNISH_STORAGE="malloc,64M"

緩存默認保存在磁盤文件中,爲了提高性能可修改爲保存在內存中;

2、定義單個後端主機

# vim/etc/varnish/default.vcl

backenddefault {

  .host = "172.16.14.2";

  .port = "80";

}

編輯修改完vcl後必須重載配置文件才能生效,但是不建議重啓varnish(會導致所有緩存清空),可使用如下方法實現:

#varnishadm -S /etc/varnish/secret -T 127.0.0.1:6082

varnish> vcl.list       //查看已有的vcl

200       

active         2 boot

varnish> vcl.loadtest1 ./default.vcl//編譯剛剛修改的vcl

200       

VCL compiled.

varnish> vcl.list//顯示剛修改的vcl已可使用

200       

active         2 boot

available      0 test1

varnish> vcl.use test1//重載修改過的vcl

200       

varnish> vcl.list//顯示重載成功,已在使用

200       

available      2 boot

active         0 test1

3、定義多個後端主機

backend NAME {

.host=

.port=

}

vcl中條件判斷的格式

單分支:

if(CONDITION) {

...;

}                

雙分支:

if(CONDITION) {

...;

}else {

...;

}

多分支:

if(CONDITION1) {

...;

}elseif (CONDITION2) {

...;

}else {

...;

}

示例:在響應報文首部添加新首部以顯示緩存的命中情況

sub vcl_deliver {

if(obj.hits>0){

setresp.http.X-Cache = "HIT";

}else {

set resp.http.X-Cache= "MISS";

}

}

網頁測試結果

wKiom1Vy3oSgfh1XAAFqMt_RwCU791.jpg

vcl中常用變量

1、任何階段(引擎)都可用的變量(圖示任何階段)

now:獲取當前時間;

.host:後端主機IP地址或主機名;

.port:後端主機端口號或服務名;

2、處理請求階段可用的變量(圖示1

client.ip:客戶端ip;

server.hostname:varnish主機名;

server.ip:varnish ip;

server.port:varnish端口號;

req.request:請求方法;

req.url:請求的url;

req.proto:請求的http協議版本;

req.backend:用於服務此次請求的後端主機;

req.backend.healthy:後端主機的健康狀態;

req.http.HEADER:引用請求報文中指定的首部;

req.can_gzip:客戶端是否能夠接受gzip壓縮格式的響應內容;

req.restarts:此請求被重啓的次數;

3varnishbackend主機發起請求前可用的變量(圖示2

bereq.request:請求方法;

bereq.url:

bereq.proto:

bereq.http.HEADER

bereq.connect_timeout: 等待與backend建立連接的超時時長;

4backend響應報文到達varnish後,放置於cache中之前可用的變量(圖示3之後4之前)

beresp.do_stream:流式響應;

beresp.do_gzip:是否壓縮之後再存入緩存;

beresp.do_gunzip:是否解壓後再存入緩存;

beresp.http.HEADER:

beresp.proto:

beresp.status:響應狀態碼;

beresp.response:響應時的原因短語;

beresp.ttl:響應對象剩餘的生存時長,單位爲second,此值可自定義修改;

beresp.backend.name:此響應報文來源backend名稱;

beresp.backend.ip

beresp..backend.port

beresp.storage:將響應報文存儲在指定的存儲後端

5、緩存對象存入cache之後可用的變量(圖示4

obj.proto

obj.status:響應狀態碼;

obj.response:響應時的原因短語;obj.ttl

obj.hits:緩存對象命中次數;

obj.http.HEADER

6、在決定對請求鍵作hash計算時可用的變量

req.hash:以指定查詢緩存的鍵做爲hash計算的鍵;

7、在爲客戶端構建響應報文時可用的變量(圖示5

resp.proto

resp.status

resp.response

resp.http.HEADER

wKioL1Vy4FOhimIoAAD2BwXp3ow120.jpg

變量適用位置及相應權限表

wKioL1Vy4GrBCimeAAE1pg7Pi0w569.jpg

Varnish檢測後端主機的健康狀態

Varnish可以檢測後端主機的健康狀態,在判定後端主機失效時能自動將其從可用後端主機列表中移除,而一旦其重新變得可用還可以自動將其設定爲可用。爲了避免誤判,Varnish在探測後端主機的健康狀態發生轉變時(比如某次探測時某後端主機突然成爲不可用狀態),通常需要連續執行幾次探測均爲新狀態纔將其標記爲轉換後的狀態。

每個後端服務器當前探測的健康狀態探測方法通過.probe進行設定,其結果可由req.backend.healthy變量獲取,也可通過varnishlog中的Backend_health查看或varnishadm的debug.health查看。

backendweb1 {

.host = "www.zrcj.com";

.probe= {

.url= "/.healthtest.html";

.interval= 1s;

.window= 5;

.threshold= 2;

}

}

.probe中的探測指令常用的有

(1).url:

探測後端主機健康狀態時請求的URL,默認爲“/”;

(2).request:

探測後端主機健康狀態時所請求內容的詳細格式,定義後,它會替換.url指定的探測方式;比如:

.request=

"GET /.healthtest.html HTTP/1.1"//使用1.1版本“GET”方法請求健康狀態檢測頁面

"Host: www.zrcj.com" //請求的主機名

"Connection: close";//不使用“keepalive”長連接

(3).window:

設定採樣範圍,在判定後端主機健康狀態時基於最近多少次的探測進行,默認是8;

(4).threshold:

設定閾值,在.window中指定的次數中,至少有多少次是成功的才判定後端主機正健康運行;默認是3;

(5).initial:

Varnish啓動時對後端主機至少需要多少次的成功探測,默認同.threshold;

(6).expected_response:

期望後端主機響應的狀態碼,一般與“.url”或“.request”結合使用,默認爲200;

(7).interval:

探測請求的發送週期,默認爲5秒;

(8).timeout:

每次探測請求的過期時長,默認爲2秒;

Varnish調度多臺後端主機

Varnish中可以使用director指令將一個或多個近似的後端主機定義爲一個邏輯組,並能以指定的調度方式來輪流將請求發送至這些主機上。不同的director可以使用同一個後端主機,而某director也可以使用“匿名”後端主機(在director中直接進行定義)。每個director都必須有其專用名,且在定義後必須在VCL中進行調用,VCL中任何可以指定後端主機的位置均可以按需將其替換爲調用某已定義的director。

定義格式

backendweb1 {

.host= "backweb1.magedu.com";

.port= "80";

}

directorwebservers random {

.retries= 5;

{

.backend= web1;

.weight  = 2;

}

{

.backend  = {

.host= "backweb2.magedu.com";

.port= "80";

}

.weight= 3;

}

}

如上示例中,web1爲顯式定義的後端主機,而webservers這個director還包含了一個“匿名”後端主機(backweb2.magedu.com)。webservers從這兩個後端主機中挑選一個主機的方法爲random,即以隨機方式挑選。

director調度算法

Varnish的director支持的挑選方法中比較簡單的有round-robin和random兩種。其中,round-robin類型沒有任何參數,只需要爲其指定各後端主機即可,挑選方式爲“輪叫”,並在某後端主機故障時不再將其視作挑選對象;random方法隨機從可用後端主機中進行挑選,每一個後端主機都需要一個.weight參數以指定其權重,同時還可以director級別使用.retries參數來設定查找一個健康後端主機時的嘗試次數。

Varnish2.1.0後,random挑選方法又多了兩種變化形式client和hash。client類型的director使用client.identity作爲挑選因子,這意味着client.identity相同的請求都將被髮送至同一個後端主機。client.identity默認爲client.ip,但也可以在VCL中將其修改爲所需要的標識符。類似地,hash類型的director使用hash數據作爲挑選因子,這意味着對同一個URL的請求將被髮往同一個後端主機,其常用於多級緩存的場景中。然而,無論是client還hash,當其傾向於使用後端主機不可用時將會重新挑選新的後端其機。

fallback定義備用服務器

directorb3 fallback {

  { .backend = www1; }

  { .backend = www2; } //will only be used if www1 is unhealthy.

  { .backend = www3; } //will only be used if both www1 and www2

}                      // are unhealthy.

範文示例

HTTP/1.1“Cache-Control”首部示例

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

默認常規設置

 sub vcl_recv {

1、設置請求報文首部,使後端主機的日誌能記錄請求的真正客戶端來源

     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;

        }

    }

2、設置拒絕指定來源的主機訪問

     if (client.ip =="172.16.250.133") {

       error 404 "Not Found";

     }

3、設置無法識別的請求方法直接送給後端主機處理

     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);

     }

4、設置可以創建緩存的請求方法

     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);

 }

使“設置1”生效的方法

修改後端主機的http日誌記錄格式

#vim /etc/httpd/conf/httpd.conf

LogFormat "%{X-Forwarded-For}i %l %u %t \"%r\" %>s %b \"%{Referer}i\"\"%{User-Agent}i\"" combined

查看後端主機日誌記錄

#tail /var/log/httpd/access_log

wKioL1Vy4N6QPbQFAAFoFpqqdvQ288.jpg

顯示修改已然生效

設置以指定主機響應指定訪問

subvcl_recv {

  if (req.http.host ~ "(?i)^(www.)?magedu.com$") {

    set req.http.host = "www.magedu.com";

    set req.backend = www;//若訪問www.magedu.com,以www主機響應

  } elsif (req.http.host ~"(?i)^p_w_picpaths.magedu.com$") {

   set req.backend = p_w_picpaths;//若訪問p_w_picpaths.magedu.com,以p_w_picpaths主機響應

  } else {

   error 404 "Unknown virtual host";//非以上兩種訪問請求,響應404錯誤

  }

}

移除單個緩存對象

purge用於清理緩存中的某特定對象及其變種(variants),因此,在有着明確要修剪的緩存對象時可以使用此種方式。HTTP協議的PURGE方法可以實現purge功能,不過,其僅能用於vcl_hit和vcl_miss中,它會釋放內存工作並移除指定緩存對象的所有Vary:-變種,並等待下一個針對此內容的客戶端請求到達時刷新此內容。另外,其一般要與return(restart)一起使用。下面是個在VCL中配置的示例。

aclpurgers {

"127.0.0.1";

"172.16.0.0"/16;

}//指明允許發起“purge”操作的主機

subvcl_recv {

if(req.request == "PURGE") {

if(!client.ip ~ purgers) {

error405 "Method not allowed";

}

return(lookup);

}

}//非允許的主機發起的“purge”請求被返回錯誤提示,允許的繼續查緩存

subvcl_hit {

if(req.request == "PURGE") {

purge;

error200 "Purged";

}

}//緩存名字,則刪除此緩存,並返回“成功,已刪除”;

subvcl_miss {

if(req.request == "PURGE") {

purge;

error404 "Not in cache";

}

}//緩存未命中,則返回“404錯誤,無此緩存”

subvcl_pass {

if(req.request == "PURGE") {

error502 "PURGE on a passed object";

}

}//purge”發錯engine,則返回“502錯誤,發送錯誤”

wKioL1Vy4WrT38TDAANa2nJxucc943.jpg

wKiom1Vy38yxmGFAAAFttYBK24s063.jpg


客戶端在發起HTTP請求時,只需要爲所請求的URL使用PURGE方法即可,其命令使用方式如下:

# curl -I -X PURGE http://varniship/path/to/someurl

注意:若要使用此設置,必須在可接受的請求方法和可緩存的請求方法中加入“PURGE”方法;

設置健康狀態檢測機制

probechk {

.url= "/index.html";

.window= 5;

.threshold= 3;

.interval= 3s;

.timeout= 1s;

}

backendappserv {

.host= "172.16.14.2";

.port= "80";

.probe= chk;

}

backendstatic {

.host= "172.16.14.3";

.port= "80";

.probe= chk;

}

兩後端主機起始狀態

wKiom1Vy4AeAYtvQAAG4BqR6scg400.jpg

移除appserv的/index.html文件後

wKioL1Vy4dqSXIhTAAGWlJkkUz8197.jpg

移回appserv的/index.html文件後

wKiom1Vy4EqDkGozAAGLYlDCtV8433.jpg

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章