varnish(筆記)

varnish: 

    vcl: varnish配置語言,配置varnish緩存策略;

        是“域”類型:

    state engine:(9個)

                    vcl_recv

                     vcl_pipe

                     vcl_pass

                     vcl_hash

                     vcl_hit

                     vcl_miss

                     vcl_fetch

                     vcl_deliver

                     vcl_error

(1)vcl_recv模塊

用於接收和處理請求。當請求成功被調用後,Varnish通過判斷請求的數據來決定如何處理請求。此模塊一般以如下幾個關鍵字結束。

pass:表示進入pass模式,把請求交給vcl_pass模塊處理。

pipe:表示進入pipe模式,把請求交給vcl_pipe模塊處理。

error code [reason]:表示把錯誤標識返回給客戶端,並放棄處理該請求。錯誤標識包括200、405等。"reason"是對錯誤的提示信息。

(2)vcl_pipe模塊

此模塊在請求進入pipe模式時被調用,用於將請求直接傳遞至後端主機,在請求和返回的內容沒有改變的情況下,也就是在當前連接未關閉時,服務器將不變的內容返回給客戶端,直到該連接被關閉。

(3)vcl_pass模塊

此模塊表示當請求被pass後,用於將請求直接傳遞至後端應用服務器。後端應用服務器在接收請求後將數據發送給客戶端,但不進行任何數據的緩存,在當前連接下每次都返回最新的內容。

(4)lookup

一個請求在vcl_recv中被lookup後,Varnish將在緩存中提取數據。如果緩存中有相應的數據,就把控制權交給vcl_hit模塊;如果緩存中沒有相應的數據,請求將被設置爲pass並將其交給vcl_miss模塊。

(5)vcl_hit模塊

執行lookup指令後,Varnish在緩存中找到請求的內容後將自動調用該模塊。

在此模塊中,deliver表示將找到的數據發送給客戶端,並把控制權交給vcl_deliver模塊。

(6)vcl_miss模塊

執行lookup後,Varnish在緩存中沒有找到請求的內容時會自動調用該方法。此模塊可以用於判斷是否需要從後端服務器獲取內容。

在此模塊中,fetch表示從後端獲取請求的數據,並把控制權交給vcl_fetch模塊。

(7)vcl_fetch模塊

在後端主機更新緩存並且獲取內容後調用該方法,接着,通過判斷獲取的內容來決定是將內容放入緩存,還是直接返回給客戶端。

(8)vcl_deliver模塊

當一個沒有被緩存的數據交付給客戶端的時候被調用。

(9)vcl_timeout 模塊

在緩存數據到期前調用此模塊。

在此模塊中,discard表示從緩存中清除到期數據。

(10)vcl_discard模塊

在緩存數據到期後或緩存空間不夠時,自動調用該模塊。

在此模塊中keep表示將數據繼續保留在緩存中。


vcl配置文件:/etc/varnish/default.vcl

重啓varnish將緩存數據清空。使用varnishadm加載


varnishadm

-S /etc/varnish/secret -T IP:PORT


---vcl.load

vcl.use

vcl.show

vcl.discard(刪除vcl)


backend storage:

malloc

file

persistent(不用)


varnish:----示例

在vcl中使用條件判斷:


單分支:

if (CONDITION) {

...;

}


雙分支:

if (CONDITION) {

...;

} else {

...;

}


多分支:

if (CONDITION1) {

...

} elseif (CONDITION2) {

...

} else {

...

}


if(obj.hits>0) {

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

} else {

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

}


常用變量:

1、在任何引擎中均可使用:

now, .host(backend), .port(backend)

now:The current time, in seconds since the epoch. When used in string context it returns a formatted string.

.host:Host name or IP address of a backend.

.port:Service name or port number of a backend.


2、用於處理請求階段(recv,hash,pipe,cache):----從客戶端發往varnish服務器

client.ip(客戶端), server.hostname(varnish主機), server.ip(varnish主機), server.port(varnish主機)

req.request:請求方法

req.url: 請求的URL

req.proto: HTTP協議版本

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

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

req.http.HEADER(替換header或者host): 引用請求報文中指定的首部; 如:req.http.host

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

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


3、varnish向backend主機發起請求前可用的變量-----從服務器發往後端服務器

bereq.request: 請求方法

bereq.url:

bereq.proto:

bereq.http.HEADER(替換header或者host)

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


4、backend主機的響應報文到達本主機(varnish)後,將其放置於cache中之前可用的變量

beresp.do_stream: 流式響應(接一個發一個,不是全部彙總之後在發);

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

beresp.do_gunzip:在存入之前解壓縮到緩存

beresp.http.HEADER(替換header或者host):

beresp.proto: 

beresp.status:響應狀態碼

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

beresp.ttl:響應對象的生存週期,單位爲second;

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

beresp.backend.ip

beresp..backend.port

beresp.storage----指定的存儲後端


5、緩存對象存入cache之後可用的變量(變量只讀)

obj.proto--The HTTP protocol version used when the object was retrieved.

obj.status---The HTTP status code returned by the server.

obj.response--The HTTP status message returned by the server.

obj.ttl--The object's remaining time to live, in seconds. obj.ttl is writable.

obj.hits--The approximate number of times the object has been delivered. A value of 0 indicates a cache miss. This variable is also available in vcl_deliver

obj.http.HEADER---The corresponding HTTP header.


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

req.hash:The hash key used to refer to an object in the cache. Used when both reading from and writing to the cache.


7、在爲客戶端準備響應報文時可用的變量

resp.proto

resp.status

resp.response

resp.http.HEADER


狀態引擎:

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

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

是否服務此請求、如何服務、使用哪個後端主機爲其提供服務;


示例:

# Drop any cookies sent to Wordpress.

sub vcl_recv {

if (!(req.url ~ "wp-(login|admin)")) {

unset req.http.cookie;(unset是撤銷變量)

}

}


sub vcl_recv {

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

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

   set req.backend = www;

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

   set req.backend = p_w_picpaths;

 } else {

   error 404 "Unknown virtual host";

 }

}

註釋:(?i)是不區分大小寫


# Drop any cookies Wordpress tries to send back to the client.

sub vcl_fetch {

if (!(req.url ~ "wp-(login|admin)")) {

unset beresp.http.set-cookie;

}

}


vcl_fetch狀態引擎:

從backend主機收到響應報文之前被調用;可return的值:

deliver

error code [reason]

restart


移除單個緩存對象


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


acl purgers {

"127.0.0.1";

"192.168.0.0"/24;

}


sub vcl_recv {

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

}

}


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

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


啓用默認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 == "PURGE" ) {

       if (!client.ip ~ purgers) {

           error 405 "Method not allowed.";

       }

    }

    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" ) {

        /* Non-RFC2616 or CONNECT which is weird. */

        return (pipe);

    }

    if (req.request != "GET" && req.request != "HEAD" && req.request != "PURGE") {

        /* We only deal with GET and HEAD by default */

        return (pass);

    }

    if (req.http.Authorization || req.http.Cookie) {

        /* Not cacheable by default */

        return (pass);

    }

    return (lookup);

}



定義要使用後端主機:

backend NAME {

.host = 

.port =

}


vcl_recv {

...

if (CONDITION) {

set req.backend = BE_NAME;

}

}


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


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


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


backend web1 {

.host = "www.magedu.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"

"Host: www.magedu.com"

"Connection: close";

(3) .window:設定在判定後端主機健康狀態時基於最近多少次的探測進行,默認是8;

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

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

(6) .expected_response:期望後端主機響應的狀態碼,默認爲200;

(7) .interval:探測請求的發送週期,默認爲5秒;

(8) .timeout:每次探測請求的過期時長,默認爲2秒;


probe healthcheck {

  .url = "/status.cgi";

  .interval = 60s;

  .timeout = 0.3 s;

  .window = 8;

  .threshold = 3;

  .initial = 3;

  .expected_response = 200;

}


backend www {

 .host = "www.example.com";

 .port = "http";

 .probe = healthcheck;

}


Varnish使用多臺後端主機


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


backend web1 {

.host = "backweb1.magedu.com";

.port = "80";

}


director webservers random {

 .retries = 5;

 {

   .backend = web1;

   .weight  = 2;

 }

 {

   .backend  = {

     .host = "backweb2.magedu.com";

 .port = "80";

   }

  .weight         = 3;

 }

}


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


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


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


另外還有一種稱作fallback的director,用於定義備用服務器,如下所示:


director b3 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.

}


在vcl_recv上,一般要用

set req.backend = DIRECTOR


例子:


backend www {

 .host = "www.magedu.com";

 .port = "80";

}


backend p_w_picpaths {

 .host = "p_w_picpaths.magedu.com";

 .port = "80";

}


sub vcl_recv {

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

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

   set req.backend = www;

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

   set req.backend = p_w_picpaths;

 } else {

   error 404 "Unknown virtual host";

 }

}


sub vcl_deliver {


 if (obj.hits > 0) {

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

 } else {

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

 }

}


sub vcl_deliver {

       if (obj.hits > 0) {

               set resp.http.X-Cache = "HIT via" + " " + server.hostname;

       } else {

               set resp.http.X-Cache = "MISS via" + " " + server.hostname;

       }

}


sub vcl_recv {

       if (req.url ~ "^/test.html$") {

               return(pass);

       }

}


sub vcl_fetch {


       if (req.request == "GET" && req.url ~ "\.(html|jpg|jpeg)$") {

               set beresp.ttl = 3600s;

       }

}


sub vcl_fetch {

if (beresp.http.cache-control !~ "s-maxage") {

if (req.url ~ "\.jpg(\?|$)") {

set beresp.ttl = 30s;

unset beresp.http.Set-Cookie;

}

if (req.url ~ "\.html(\?|$)") {

set beresp.ttl = 10s;

unset beresp.http.Set-Cookie;

}

} else {

if (beresp.ttl > 0s) {

unset beresp.http.Set-Cookie;

}

}

}


sub vcl_error {

synthetic "<html><body><!-- Something was wrong! --></body></html>";

set obj.status = 200;

return (deliver);

}


生產環境案例一則:


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

}



varnish的線程模型:

cache-worker線程

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

ban luker:

acceptor:

epoll:線程池管理器

expire:清理過期緩存


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

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

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

thread_pool_min

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

thread_pool_purge_delay:空閒多少時間後清理空閒線程。最短保留最少線程。


varnish的param查看及改變:

param.show [-l] [param]

param.set [param] [value]


varnish的命令行工具:

varnishadm, 


varnishtop: 內存日誌區域查看工具

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選項進行過濾;


-I regexp: 僅顯示被模式匹配到的條目

-X regexp:僅顯示不被模式匹配到的條目

-C: 忽略字符大小寫;

-d: 顯示已有日誌;


varnishstat:

-f field, field, ...

-l: 列出所有可用字段

-x: xml輸出格式 

-j: json輸出格式


varnishlog, varnishncsa


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