Varnish性能、優勢、配置優化解讀

1.Varnish是一個HTTP加速緩存代理,性能優勢取代了之前squid的緩存機制,4臺varnish等於12臺的squid,它性能穩定,效率高,目前很多電商企業都是利用Varnish達到較高的讀取速度。

    Varnish的工作原理

當客戶端第一次訪問的時候,它直接通過數據庫讀取的目標,並保存目標在自己的緩存空間裏,當第二次訪問的時候,即可快速的返回給客戶,如果沒有訪問到數據,直接到到後端服務器(nginx)獲取,並返回給客戶,這樣的機制就達到了對數據庫的減壓效果,減速數據庫的讀寫壓力。當它緩存空間滿的話,會自動清除舊的數據,釋放緩存的過程是這樣的:有一個超時線程,檢測緩存中所有 object 的生存期,如果超初設定的 TTL(Time To Live)沒有被訪問,就刪除之,並且釋放相應的結構體及存儲內存。

2.Varnish配置文件配置

vcl 4.0;
import directors;
import std;
backend web01 {
    .host = "127.0.0.1";  # <span style="line-height: 22.3872px; font-family: SimSun;">指定後端主機的IP地址或者域名</span>
    .port = "8080";       # 指定後端主機的服務端口
    .probe = {
        .url = "/";       # 哪個 url 需要 varnish 請求
        .interval = 10s;  # 檢查的時間間隔
        .timeout = 2s;    # 等待多長時間超時
        .window = 3;      # 維持 3 個 sliding window 的結果
        .threshold = 3;   # 至少有三次 window 是成功的,就宣告 backend 健康#director當後端服務器宕機的時候,能給健康的主機發送請求,如果兩臺都宕了,varnish也照常使用
director example_director round-robin {
      {
              .backend = server1;
      }
      # server2
      {
              .backend = server2;
      }
}
    }
}

backend web02 {
    .host = "127.0.0.1";
    .port = "8081";
    .probe = {
        .url = "/";
        .interval = 10s;
        .timeout = 2s;
        .window = 3;
        .threshold = 3;
    }
}
#director當後端服務器宕機的時候,能給健康的主機發送請求,如果兩臺都宕了,varnish也照常使用
director example_director round-robin {
      {
              .backend = server1;
      }
      # server2
      {
              .backend = server2;
      }
}
#只允許本機控制,限制其餘網段不能控制。
acl manager {
    "localhost";  
    ! "192.168.2.0"/24;
    ! "192.168.12.0"/24;
    ! "192.168.2.200";
}
sub vcl_init {
        new www = directors.round_robin();
                www.add_backend(web01);
                www.add_backend(web02);
        }


sub vcl_recv {
    if (req.http.host !~ "(?).xjh.com$") {
         set req.backend_hint = www.backend();
    }
    if (req.http.host !~ "(?).xjh.com$") {
        return (synth(403,"Forbidden"));    
    }
    if (req.http.Upgrade ~ "(?i)websocket") {
             return (pipe);
         }
    #if (req.http.host ~ "xjh.com") {
        #    set req.http.host = "www.qa.xjh.com";
    #}
    #if (req.http.User-Agent ~ "(?i)iphone" {
        #set req.http.X-UA-Device = "mobile-iphone";
        #}
    if (req.method == "PURGE" || (req.http.Pragma ~ "no-cache" && client.ip ~ manager)) {
            if (client.ip !~ manager) {
                   return(synth(403, "Access denied."));
            }    
        ban("req.http.host == " + req.http.host +" && req.url == " + req.url);
        return(synth(200, "Ban added"));
      }
    if (req.url ~ "\.(php|asp|aspx|jsp|do|ashx|shtml)($|\?)") {
            std.log("dynamic URI - url=" + req.url);
            return (pass);
        }
        if (req.url ~ "\.(css|js|html|htm|bmp|png|gif|jpg|jpeg|ico|gz|tgz|bz2|tbz|zip|rar|mp3|mp4|ogg|swf|flv)($|\?)") {
            std.log("static URI - url=" + req.url);
            unset req.http.cookie;
            return (hash);
        }
        if (req.method == "GET" && req.method == "HEAD") {
            std.log("METHOD URI - url=" + req.url);
                return (hash);
        }
        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.method != "GET" && 
              req.method != "HEAD" && 
            req.method != "PUT" && 
            req.method != "POST" && 
            req.method != "TRACE" && 
            req.method != "OPTIONS" && 
            req.method != "PATCH" && 
            req.method != "DELETE") {        
                return (pipe);
        }


        if (req.http.Authorization) {
            return (pass);
        }


        if (req.http.Accept-Encoding) {
            if (req.url ~ "\.(bmp|png|gif|jpg|jpeg|ico|gz|tgz|bz2|tbz|zip|rar|mp3|mp4|ogg|swf|flv)$") {
                unset req.http.Accept-Encoding;        
            } elseif (req.http.Accept-Encoding ~ "gzip") {
                set req.http.Accept-Encoding = "gzip";
            } elseif (req.http.Accept-Encoding ~ "deflate") {
                set req.http.Accept-Encoding = "deflate";
            } else {
                unset req.http.Accept-Encoding;
            }
        }
    #if (req.url ~ "^/images") {
       #    unset req.http.cookie;
      #}
        std.log("OTHER URI - url=" + req.url);
    return (hash);
}
sub vcl_pipe {
     if (req.http.upgrade) {
            set bereq.http.upgrade = req.http.upgrade;
         }
}
sub vcl_hash {
        hash_data(req.url);
       if (req.http.host) {
            hash_data(req.http.host);
        } else {
            hash_data(server.ip);
        }
        std.log("HASH URI - url=" + req.url);
        return (lookup);
}
sub vcl_hit {
        std.log("HIT URI - url=" + req.url);
      if (obj.ttl > 0s) {
               return (deliver);
       }
      if (obj.ttl + obj.grace > 0s) {
               return (deliver);
       }
       if (!std.healthy(req.backend_hint) && (obj.ttl + obj.grace > 0s)) {
               return (deliver);
       } else {
                  return (fetch); 
      }
    return (deliver);
}
sub vcl_miss {
    std.log("Miss URI - url=" + req.url);
    return (fetch);
}
sub vcl_backend_response {
    #set beresp.ttl = std.duration(regsub(beresp.http.Cache-Control, ".*s-maxage=([0-9]+).*", "\1") + "s", 0s);
        if (beresp.ttl > 0s) {
            unset beresp.http.Set-Cookie;
        }


        if (beresp.http.Set-Cookie) {
            set beresp.uncacheable = true;
            return (deliver);
        }


        if (beresp.http.Cache-Control && beresp.ttl > 0s) {
            set beresp.grace = 1m;
            unset beresp.http.Set-Cookie;
        }


        if (beresp.http.Content-Length ~ "[0-9]{8,}") {
            set bereq.http.x-pipe = "1";
            return (retry);
        }


        if (bereq.url ~ "\.(php|asp|aspx|jsp|do|ashx|shtml)($|\?)") {
            set beresp.uncacheable = true;
            return (deliver);
        }


        if (bereq.url ~ "\.(css|js|html|htm|bmp|png|gif|jpg|jpeg|ico)($|\?)") {
        unset beresp.http.set-cookie;
               set beresp.ttl = 12h;
        } elseif (bereq.url ~ "\.(gz|tgz|bz2|tbz|zip|rar|mp3|mp4|ogg|swf|flv)($|\?)") {
            set beresp.ttl = 30m;
        } else {
            set beresp.ttl = 10m;
        }


        #if ((!beresp.http.Cache-Control && !beresp.http.Expires) || 
        #    beresp.http.Pragma ~ "no-cache" || 
        #    beresp.http.Cache-Control ~ "(no-cache|no-store|private)") {
        #    set beresp.ttl = 120s;
        #    set beresp.uncacheable = true;
        #    return (deliver);
        #}


        if (beresp.ttl <= 0s || beresp.http.Set-Cookie || beresp.http.Vary == "*") {
        unset beresp.http.set-cookie;
               set beresp.ttl = 120s;
            set beresp.uncacheable = true;
            return (deliver);
        }


        return (deliver);
    set beresp.grace = 2m;
}


sub vcl_deliver {
    if (obj.hits > 0) {
            set resp.http.X-Cache = "HIT from " + req.http.host;
            set resp.http.X-Cache-Hits = obj.hits;
        } else {
            set resp.http.X-Cache = "MISS from " + req.http.host;
        }
        unset resp.http.X-Powered-By;
        unset resp.http.Server;
        unset resp.http.Via;
        unset resp.http.X-Varnish;
        unset resp.http.Age;
        unset resp.http.qq;
}
sub vcl_backend_error {
    if (beresp.status == 500 || 
        beresp.status == 501 || 
        beresp.status == 502 || 
        beresp.status == 503 || 
        beresp.status == 504) {
        return (retry);
    }
}


sub vcl_fini {
    return (ok);
}


3.故障排錯

如果啓動不了Varnish有可能是VCL編譯有問題,或者就是有些插件沒裝。需要查看log日誌。

4.Varnish提升緩存

Varnish的一個關鍵特色就是它有能力防禦web和應用服務器宕機。

1. 當幾個客戶端請求同一個頁面的時候,varnish只發送一個請求到後端服務器,然後讓那個其他幾個請求掛起等待返回結果,返回結果後,複製請求的結果發送給客戶端.如果您的服務每秒有數千萬的點擊率,那麼這個隊列是龐大的,沒有用戶喜歡等待服務器響應。爲了使用過期的cache給用戶提供服務,我們需要增加他們的TTL,保存所有cache中的內容在TTL過期以後30分鐘不刪除,使用以下VCL:

sub vcl_fetch {
 set beresp.grace = 30m;
}        
        2. Varnish還不會使用過期的目標給用戶提供服務,所以我們需要配置以下代碼,在cache過期後的15秒內,使用舊的內容提供服務:

sub vcl_recv {
 set req.grace = 15s;
}
        3. 你會考慮爲什麼要多保存過去的內容30分鐘?當然,如果你使用了健康檢查,你可以通過健康狀態設置保存的時間:

if (! req.backend.healthy) {
   set req.grace = 5m;
} else {
   set req.grace = 15s;
}
         Saint mode
        4. 有時候,服務器很古怪,他們發出隨機錯誤,您需要通知varnish使用更加優雅的方式處理它,這種方式叫神聖模式(saint mode)。Saint mode允許您拋棄一個後端服務器或者另一個嘗試的後端服務器或者cache中服務陳舊的內容。讓我們看看VCL中如何開啓這個功能的:

sub vcl_fetch {
 if (beresp.status == 500) {
    set beresp.saintmode = 10s;
    restart;
 }
 set beresp.grace = 5m;
}
         當我們beresp.saintmode 10秒,這個url的請求varnish將在10秒內不訪問服務器。一個黑名單,或多或少。一個restart就會執行,如果您有其他的後端有能力提供這些服務器內容,varnish會嘗試他們。當您沒有可用的後端服務器,varnish將使用它過期的cache提供服務內容。

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