varnish配置語言(2)


知識來源爲官方文檔:
《Varnish用戶指南》
《Varnish4.0穩定版參考指南》java

1. Backend servers

Varnish有一個「後端」或「原始」服務器的概念,指真正提供內容的 web 服務器,或稱爲"upstream server"
後端 web 服務器經過 Varnish 的緩存功能對訪問進行加速。git

首先,告訴Varnish在哪裏能夠找到它的後端。編輯緩存策略配置文件"$varnish_home/default.vcl"github

在頂部的某個地方會有一個看起來像這樣的部分。web

vcl 4.0;

backend default {
    .host = "127.0.0.1";
    .port = "8080";
}

這裏定義了一個後端服務器,名爲 default。當 Varnish 須要從後端獲取內容,它會鏈接到 127.0.0.1:8080。正則表達式

Varnish 可定義多個後端服務器,而且能夠聯合多個後端服務器爲一個後端集羣,進行負載均衡。後端

2. 多個後端

在某些狀況下,您可能須要清漆來緩存來自多個服務器的內容。您可能但願Varnish將全部URL映射到一個主機上,或者不但願這樣作。有不少選擇。瀏覽器

結合上一節配置的基礎上,假設咱們須要在咱們的PHP網站中引入一個Java應用程序。假設咱們的Java應用程序應該處理以/Java/開頭的URL。咱們設法讓它啓動並在端口8000上運行。緩存

backend java {
    .host = "127.0.0.1";
    .port = "8000";
}

如今咱們須要告訴Varnish將差別URL發送到哪裏。調用vcl_recv。安全

在 vcl_recv 中,咱們根據請求的 URL,給請求打上不一樣的 hint,使其發往指定的後端服務器。

sub vcl_recv {
    if (req.url ~ "^/java/") {
        set req.backend_hint = java;
    } else {
        set req.backend_hint = default;
    }
}

about:variable "req"

若是要將移動設備發出的請求,發給專門的後端服務器,能夠作這樣的判斷:

if (req.http.User-agent ~ /mobile/) ..

若是沒有明確的後端選擇,varnish將繼續使用默認的後端。若是沒有名爲default的後端,則在vcl中找到的第一個後端將用做默認後端。

3. Varnish 中的後端服務器和虛擬主機

咱們在 vcl_recv 中爲 HTTP 請求設定路由,若是但願基於「虛擬主機」作路由,那麼就對 req.http.host 變量作檢查,好比:

sub vcl_recv {
    if (req.http.host ~ "foo.com") {
        set req.backend_hint = foo;
    } elsif (req.http.host ~ "bar.com") {
        set req.backend_hint = bar;
    }
}

第一個正則表達式,能匹配 "foo.com", "www.foo.com", "zoop.foo.com",以及其餘以 "foo.com" 結尾的主機名。

也可使用 == 進行準確的匹配:

sub vcl_recv {
    if (req.http.host == "foo.com" || req.http.host == "www.foo.com") {
        set req.backend_hint = foo;
    }
}

4. 調度器

咱們能夠將多個後端服務器組成一組,Varnish 中的組被稱爲 directors。將多個後端服務器組成一組能夠提升性能、容錯性和伸縮性。

咱們定義多個後端服務器,而後組成一個組。這要求你加載一個 VMOD:directors 模塊。而後在 vcl_init 中調用一些 actions:

import directors; # load the directors

backend server1 {
    .host = "192.168.0.10";
}
backend server2 {
    .host = "192.168.0.11";
}

sub vcl_init {
    new bar = directors.round_robin();
    bar.add_backend(server1);
    bar.add_backend(server2);
}

sub vcl_recv {
    # send all traffic to the bar director:
    set req.backend_hint = bar.backend();
}

這個 director 是一個輪詢調度器。另外還有一個隨機調度器。若是一個後端服務器失效,Varnish 能夠檢查到,而後再也不調度請求給該後端服務器。

5. 健康檢查

咱們來設置一個包含兩個後端服務器的 director,而且設置健康檢查,首先咱們定義後端服務器:

backend server1 {
    .host = "server1.example.com";
    .probe = {
        .url = "/";
        .timeout = 1s;
        .interval = 5s;
        .window = 5;
        .threshold = 3;
    }
}

backend server2 {
    .host = "server2.example.com";
    .probe = {
        .url = "/";
        .timeout = 1s;
        .interval = 5s;
        .window = 5;
        .threshold = 3;
    }
}

probe 引入了健康檢查。此例中,Varnish 每 5 秒進行一次檢查,檢查超時爲 1 秒。若是最近 5 次檢查中,有 3+ 次成功,該後端被標記爲 「healthy」,不然被標記爲 「sick」。發起檢查,實際上是對後端服務器地址 http://host/ 發起一個 GET 請求.

更多信息可參考Probes

咱們如今來定義 director:

import directors;

sub vcl_init {
    new vdir = directors.round_robin();
    vdir.add_backend(server1);
    vdir.add_backend(server2);
}

如今可使用 vdir 做爲 backend_hint,參考上一小節的描述,好比:

sub vcl_recv {
    # send all traffic to the vdir director
    set req.backend_hint = vdir.backend(); 
}

Varnish 不會將流量調度給被認爲不健康的後端主機。

若是全部後端服務器失效了,Varnish還能夠提供陳舊的內容。參考Grace mode and keep

請注意,Varnish 對全部加載的 VCL 進行檢查,並且會將相同的檢查進行合併。因此若是你加載了不少的 VCL,注意不要修改 probe 配置。卸載 VCL 以後,檢查不會繼續,更多信息請求參考:ref:reference-vcl-director

6. Hashing

Varnish 對內容作緩存時,也要存下對應的 hash key。
在默認的狀況下,hash key 是對 Host 首部字段的值或者 IP 地址作 hash 計算,而且對 URL 作 hash 計算,獲得的值,爲 hash key。

默認default.vcl:

sub vcl_hash {
    hash_data(req.url);
    if (req.http.host) {
        hash_data(req.http.host);
    } else {
        hash_data(server.ip);
    }
    return (lookup);
}

在默認 vcl_hash 中,首先對 URL(req.url) 計算 hash 值,而後繼續對 Host(req.http.host) 字段或者 IP地址計算 hash 值。

瀏覽器傾向於使用小寫的主機名,而Varnish在對Hostname或URL進行hash處理以前不會將其小寫,所以從理論上講,具備「 Varnish.org/」和「 varnish.org/」的Host會輸入不一樣的緩存條目。

這裏對 hash 值的計算是逐步累加的,您能夠更改hash中的內容。這樣,您可使Varnish根據任意條件爲不一樣的客戶提供不一樣的內容。

好比,經過客戶端的 IP 地址獲知其所在的國家,基於這個提供不一樣的語言的網站頁面(須要加載一些 VMOD 獲知國家的代碼),好比:

In vcl_recv:

set req.http.X-Country-Code = geoip.lookup(client.ip)

And then add a vcl_hash:

sub vcl_hash {
    hash_data(req.http.X-Country-Code);
}

vcl_* 系列子例程中的默認代碼會在執行完咱們本身編寫的代碼後自動執行,因此在 vcl_hash 中,默認狀況下,VCL 會自動對 host 和 URL 計算 hash 值,這部分不須要咱們去作。

但要注意,不要輕易調用 return(lookup),由於它會終止默認 VCL 的執行,可能會致使 hash key 計算時,沒有加入 host 和 URL。

7. 優雅模式 Grace mode 和 keep

有時您但願varnish爲有些陳舊的內容提供服務,而不是等待來自後端的新對象。例如,您運行一個新聞站點,若是這給您的站點提供了更快的加載時間,那麼爲幾秒鐘前的主頁提供服務就不是問題。
在Varnish中,這是經過使用grace模式實現的。一個相關的想法是保持,這也解釋在這裏。

7.1 寬限模式

當幾個客戶端請求同一個頁面的時候,varnish只發送一個請求到後端服務器,而後讓那個其餘幾個請求掛起等待返回結果,返回結果後,複製請求的結果發送給客戶端。在一些產品中,這個可能叫請求合併。varnish這些都是自動完成的。

若是您的服務每秒有數千萬的點擊率,那麼這個隊列是龐大的。這就有兩個潛在的問題:一個是驚羣問題——即忽然釋放大量線程去複製後端的結果,可能會致使負載急劇上升。第二個問題就是沒有用戶喜歡等待服務器響應。爲了解決這個問題,咱們可讓varnish保持對象在緩存中,讓它不失效,這樣就可使用緩存中內容返回給客戶端,而不用擔憂等待。
因此爲了使用過時的 cache 給用戶提供服務,咱們須要增長他們的 TTL,保存全部cache 中的內容在 TTL過時之後依然能夠保持2分鐘

sub vcl_backend_response {
    set beresp.grace = 2m;
}

如今Varnish容許在對象過時後2分鐘內提供給客戶端。同時varnish也將刷新這個對象。刷新動做是異步發生的,新的對象將替換老對象。

你能夠在vcl_hit中添加代碼來影響這種邏輯的運行,默認看起來是這樣的:

sub vcl_hit {
   if (obj.ttl >= 0s) {
       // A pure unadultered hit, deliver it
       return (deliver);
   }
   if (obj.ttl + obj.grace > 0s) {
       // Object is in grace, deliver it
       // Automatically triggers a background fetch
       return (deliver);
   }
   // fetch & deliver once we get the result
   return (fetch);
}

grace邏輯在這裏很明顯。若是你開啓了健康檢查,你能夠檢查後端是否出了問題,而後直接寬限對象失效時間便可。用以下的if語句替換掉上面的第二個if語句塊:

if (!std.healthy(req.backend_hint) && (obj.ttl + obj.grace > 0s)) {
      return (deliver);
} else {
      return (fetch);
}

綜上所述,優雅模式解決了兩個問題:

  • 提供舊內容給客戶端以免請求堆積。
  • 若是你容許varnish能夠提供過時的內容。

7.2 keep

設置對象的keep會告訴Varnish,它應該將對象保留在緩存中一段時間。設置keep的緣由是使用該對象構造條件GET後端請求(具備If-Modified-Since:和/或Ìf-None-Match:標頭),從而容許後端以304 Not Modified響應進行回覆,可能在後端效率更高,並節省了從新傳輸未更改的主體的麻煩。

這些值是可加的,所以,若是寬限期是10秒,保持時間是1分鐘,則TTL過時後,對象將在緩存中保留70秒。

7.3 設置寬限期並保持時間

咱們可使用VCL使Varnish在全部對象的TTL以外保持10分鐘,寬限期爲2分鐘:

sub vcl_backend_response {
     set beresp.grace = 2m;
     set beresp.keep = 8m;
}

7.4 寬限期和保持的效果

對於大多數用戶而言,爲每一個對象設置默認寬限度和/或合適的寬限度就足夠了。默認的VCL將作正確的事情,並如上所述執行操做。可是,若是要自定義清漆的行爲,則應瞭解有關其工做原理的一些詳細信息。

當結尾爲(這是默認行爲)時,Varnish會在其緩存中查找匹配的對象。而後,若是僅發現其TTL耗盡的對象,則Varnish將考慮如下事項:sub vcl_recvreturn (lookup)

  • 是否已經有對該對象的正在進行的後端請求?
  • 對象是否在寬限期內?

而後,Varnish使用如下規則作出反應:

  • 若是寬限期已用完,而且沒有正在進行的後端請求,那麼將當即被調用,而且該對象將用做304候選對象。sub vcl_miss
  • 若是寬限期已用完,而且有一個正在進行的後端請求,則該請求將等待,直到後端請求完成。
  • 若是沒有對該對象的後端請求,則會安排一個。
  • 假設對象將被交付,則當即調用。sub vcl_hit

請注意,後端獲取異步進行,新對象進入其中的那一刻將替換咱們已經擁有的對象。

若是您未定義本身的,則使用默認的。看起來像這樣:sub vcl_hit

sub vcl_hit {
     if (obj.ttl >= 0s) {
          // A pure unadulterated hit, deliver it
          return (deliver);
     }
     if (obj.ttl + obj.grace > 0s) {
          // Object is in grace, deliver it
          // Automatically triggers a background fetch
          return (deliver);
     }
     // fetch & deliver once we get the result
     return (miss);
}

內置VCL的效果實際上等同於如下內容:

sub vcl_hit {
     return (deliver);
}

這是由於總會評估爲true。可是,VCL照原樣向用戶展現瞭如何區分純匹配和寬限匹配。在下一個主要版本的Varnish中,計劃將默認VCL更改成後者的較短版本。obj.ttl + obj.grace > 0s

7.5 不工做的後端服務器

Varnish的一個關鍵特性是,它可以防止web服務器和應用服務器的錯誤行爲。

若是啓用了健康檢查,您能夠檢查後端是否生病,並在grace時修改行爲。這能夠經過如下方式實現:

sub vcl_backend_response {
     set beresp.grace = 24h;
     // no keep - the grace should be enough for 304 candidates
}

sub vcl_recv {
     if (std.healthy(req.backend_hint)) {
          // change the behavior for healthy backends: Cap grace to 10s
          set req.grace = 10s;
     }
}

7.6 小結

寬限模式容許Varnish向客戶端交付稍微陳舊的內容,同時從後端獲取新版本。結果是更快的加載時間和更低的成本。

能夠經過設置限制查找期間的寬限期req.grace,而後更改涉及寬限期的行爲。一般,這樣作是爲了根據後端的運行情況更改有效寬限期。

8. retry Return Action

  • 適用於vcl_backend_response和vcl_backend_error
  • 從新輸入 vcl_backend_fetch
  • 所作的任何更改都將保留
  • 參數max_retries安全防範無限循環
  • 計數器bereq.retries記錄了多少次重試
sub vcl_backend_response {
    if (beresp.status == 503) {
        return (retry);
    }
}

該retry返回動做是可用的vcl_backend_response和vcl_backend_error。此操做將從新進入vcl_backend_fetch子例程。這僅影響後端線程,不影響客戶端處理。

當後端沒法響應時,您可能要使用此操做。這樣,Varnish能夠將請求重試到另外一個後端。爲此,您必須定義多個後端。

您可使用導向器讓Varnish選擇下一個後端進行嘗試。或者,您能夠bereq.backend用來專門選擇另外一個後端。

return (retry)遞增bereq.retries計數器。若是重試次數大於max_retries,則控制權傳遞給vcl_backend_error。

**注**:在Varnish 3.0中,在後端響應失敗後能夠return (restart)。這如今稱爲return(retry),並跳轉回vcl_backend_fetch。

9. Saint Mode

Saint模式被實現爲具備如下功能的後端控制器:

  • 細粒度的健康檢查;維護對象與後端之間關係的黑名單
  • 對象具備黑名單TTL
  • 黑名單中的後端具備相關對象的閾值
  • 能夠選擇對象低於閾值的後端來服務其餘對象
  • 對於全部對象,超過閾值的後端對象將被標記爲全部對象的病態

在Varnish Cache 4.1或更高版本中可用
Saint模式經過標記特定對象的後端疾病來補充常規的「 健康檢查」。聖徒模式是一種VMOD,可維護對象和相關後端的黑名單。每一個列入黑名單的對象都有一個TTL,表示它停留在黑名單中的時間。

若是後端的黑名單對象數低於閾值,則該後端被認爲部分生病。對列入黑名單的對象的請求可能會發送到另外一個後端。當後端的列入黑名單的對象數超過閾值時,對於全部請求,該後端都將標記爲「不正常」。

如下vcl/saintmode.vcl是saint模式的典型用法。在此示例中,具備500響應狀態的請求將重試到另外一個後端。

vcl 4.0;

import saintmode;
import directors;

backend server1 { .host = "192.0.2.11"; .port = "80"; }
backend server2 { .host = "192.0.2.12"; .port = "80"; }

sub vcl_init {
        # create two saint mode backends with threshold of 5 blacklisted objects
        new sm1 = saintmode.saintmode(server1, 5);
        new sm2 = saintmode.saintmode(server2, 5);

        # group the backends in the same cluster
        new fb = directors.fallback();
        fb.add_backend(sm1.backend());
        fb.add_backend(sm2.backend());
}

sub vcl_backend_fetch {
        # get healthy backend from director
        set bereq.backend = fb.backend();
}

sub vcl_backend_response {
        if (beresp.status > 500) {
                # the failing backend is blacklisted 5 seconds
                saintmode.blacklist(5s);
                # retry request in a different backend
                return (retry);
        }
}

一種替代方法是使用陳舊對象構建響應。對於這一點,你會return(abandon),restart請求中vcl_synth,檢查req.restarts中vcl_recv。要更好地瞭解操做方法,請查看https://github.com/fgsch/vcl-snippets/blob/master/v4/stale-if-error.vcl中的stale-if-error代碼段。

對聖徒模式的細粒度檢查有助於發現後端發生故障的問題。例如,若是對對象foo的請求返回不帶內容()的HTTP響應,則能夠將該特定對象列入特定後端的黑名單。您也可使用打印對象並將其過濾。200 OKContent-Length = 0std.logvarnishlog

**注意**:有關更多信息,請參閱https://github.com/varnish/varnish-modules/blob/master/docs/vmod_saintmode.rst中的文檔。

10. 調整後端屬性

若是後端沒有足夠的資源,那麼最好設置max_connections。所以,一個特定的後端處理有限數量的併發鏈接。全部後端特定的計時器均可以做爲參數使用,而且能夠在VCL的後端特定級別上重寫。

Tip Varnish只接受解析爲最多一個IPv4地址和一個IPv6地址的後端服務器的主機名。參數prefer_ipv6定義Varnish更喜歡哪一個IP地址。

backend default {
    .host = "localhost";
    .port = "80";
    .connect_timeout = 0.5s;
    .first_byte_timeout = 20s;
    .between_bytes_timeout = 5s;
    .max_connections = 50;
}

11. 使用 inline C to 擴展 Varnish

(Here there be dragons. Big and mean ones.),不建議使用,有興趣參考官方《 inline C擴展varnish》

#include語句必須在子例程以外。
C { 
        #include <syslog.h> 
} C

sub vcl_something { 
        C { 
                syslog (LOG_INFO , 「在VCL線XX上發生了什麼。」 ); 
        } C 
}

VCL示例及其餘內容,請自行參照《Varnish4.1官方文檔》


  [sleepy↓]

相關文章
相關標籤/搜索