天天看點

Varnish詳解與實戰

一、簡介:

Varnish 是一款高性能且開源的反向代理伺服器和 HTTP 加速器,其采用全新的軟體體系機構,和現在的硬體體系緊密配合,與傳統的 squid 相比,varnish 具有穩定,且效率更高,資源占用更少等優點。

Varnish的官網為https://www.varnish-cache.org,rpm,rpm包的下載下傳位置為:http://repo.varnish-cache.org。

二、varnish結構特點:

1、varnish結構

三大部分,用戶端,varnish處理,varnish日志。如圖

2、varnish特點:

     varnish主要運作兩個程序:Management程序和Child程序(也叫Cache程序)。

Varnish詳解與實戰

     Management程序主要實作應用新的配置、編譯VCL、監控varnish、初始化varnish以及提供一個指令行接口等。Management程序會每隔一段時間探測一下Child程序以判斷其是否正常運作,如果在指定的時長内未得到Child程序的回應,Mangagement将會重新開機此Child程序。

     Child程序包含多種類型的線程,關鍵點有:

          Acceptor程序:接受新的連接配接請求并響應

          worker程序:child程序會為每個使用者啟動一個worker程序,是以,在高并發的場景中可能會出現數百個worker程序甚至更多

          Object Expiry程序:從緩存中清理過期内容

     Varnish依賴“工作區(workspace)”以降低線程在申請或修改記憶體時出現競争的可能性。在varnish内部有多種不同的工作區,其中最關鍵的當屬用于管理會話資料的session工作區。

3、varnish日志:

   varnish共享記憶體大小一般為90M,其分為兩部分,計數器和為用戶端請求的資料。varnish通過varnishlog、varnishncsa、varnishstst等來分析共享記憶體日志中的資訊并能夠以指定的方式進行顯示

4、varnish缺點:

    varnish在高并發狀态下,CPU、I/O和記憶體等資源的開銷高于Squid。Varnish的程序一旦挂起、崩潰或者重新開機,緩存的資料都會從記憶體中釋放出來。此時的所有請求都會被發送到後端應用伺服器上,在高并發的情況下,就會給後端伺服器造成很大壓力。

三、varnish的工作原理及工作流程

工作原理:

    1、Varnish 與一般伺服器軟體一樣,分為master 程序和child 程序。master程序讀入存儲配置檔案,調用合适的存儲類型,然後建立/ 讀入相應大小的緩存檔案,接着master 初始化管理該存儲空間的結構體,然後fork 并監控child 程序。child程序在主線程的初始化的過程中,将前面打開的存儲檔案整個mmap 到記憶體中,此時建立并初始化空閑結構體,挂到存儲管理結構體,以待配置設定。child程序配置設定若幹線程進行工作,主要包括一些管理線程和很多worker 線程。

    2、開始真正的工作,varnish的某個負責接收新HTTP 連接配接線程開始等待使用者,如果有新的HTTP連接配接過來,它總負責接收,然後喚醒某個等待中的線程,并把具體的處理過程交給它。Worker線程讀入HTTP 請求的URI,查找已有的object,如果命中則直接傳回并回複使用者。如果沒有命中,則需要将所請求的内容,從後端伺服器中取過來,存到緩存中,然後再回複。

    3、配置設定緩存的過程是這樣的:它根據所讀到object 的大小,建立相應大小的緩存檔案。為了讀寫友善,程式會把每個object的大小變為最接近其大小的記憶體頁面倍數。然後從現有的空閑存儲結構體中查找,找到最合适的大小的空閑存儲塊,配置設定給它。如果空閑塊沒有用完,就把多餘的記憶體另外組成一個空閑存儲塊,挂到管理結構體上。如果緩存已滿,就根據LRU 機制,把最舊的object 釋放掉。

    4、釋放緩存的過程是這樣的:有一個逾時線程,檢測緩存中所有object 的生存期,如果超初設定的TTL(Time To Live)沒有被通路,就删除之,并且釋放相應的結構體及存儲記憶體。注意釋放時會檢查該存儲記憶體塊前面或後面的空閑記憶體塊,如果前面或後面的空閑記憶體和該釋放記憶體是連續的,就将它們合并成更大一塊記憶體。

    5、整個檔案緩存的管理,沒有考慮檔案與記憶體的關系,實際上是将所有的object 都考慮是在記憶體中,如果系統記憶體不足,系統會自動将其換到swap 空間,而不需要varnish 程式去控制。

    官方提供的工作流程圖:

Varnish詳解與實戰

四、varnish實戰:

1、拓撲圖:

Varnish詳解與實戰

2、安裝配置:    

# 安裝包下載下傳位址:http://repo.varnish-cache.org/redhat/varnish-4.0/el6/

yum -y install varnish-3.0.5-1.el6.x86_64.rpm varnish-docs-3.0.5-1.el6.x86_64.rpm varnish-libs-3.0.5-1.el6.x86_64.rpm     
vim /etc/sysconfig/varnish # 編輯配置檔案,修改如下項
VARNISH_LISTEN_PORT=80 # varnish監聽端口改為80端口
VARNISH_STORAGE_SIZE=64M # 此值根據自身情況調整,測試環境可調低此值
VARNISH_STORAGE="malloc,${VARNISH_STORAGE_SIZE}" #使用malloc(即記憶體)作為緩存對象存儲方式;
service varnish start # 啟動varnish
varnishadm -S /etc/varnish/secret -T 127.0.0.1:6082 # 登入管理指令行
varnish> vcl.list                 # 列出所有的配置
varnish> vcl.load test1 ./test.vcl  # 加載編譯新配置,test1是配置名,test.vcl是配置檔案
varnish> vcl.use test1            # 使用配置,需指定配置名,目前使用的配置以最後一次vcl.use為準
varnish> vcl.show test1           # 顯示配置内容,需指定配置名      

3、主配置檔案分析

vim /etc/varnish/default.vcl
# This is a basic VCL configuration file for varnish. See the vcl(7)
# man page for details on VCL syntax and semantics.
#
# Default backend definition. Set this to point to your content
# server.
#      
import directors;    # 導入directors子產品;
probe backend_healthcheck {    # 建立健康監測機制;
    .url = "/health.html";
    .window = 5;
    .threshold = 2;
    .interval = 3s;
}
backend web1 {    # 建立後端主機;
    .host = "192.168.1.4";
    .port = "80";
    .probe = backend_healthcheck;
}
backend web2 {
    .host = "192.168.1.5";
    .port = "80";
    .probe = backend_healthcheck;
}
backend img1 {
    .host = "192.168.1.4";
    .port = "4040";
    .probe = backend_healthcheck;
}
backend img2 {
    .host = "192.168.1.5";
    .port = "4040";
    .probe = backend_healthcheck;
}
sub vcl_init {    # 建立後端主機組,即directors;
    new web_cluster = directors.random();
    web_cluster.add_backend(web1,1.0);
    web_cluster.add_backend(web2,1.0);
    new img_cluster = directors.random();
    img_cluster.add_backend(img1,1.0);
    img_cluster.add_backend(img2,1.0);
}
acl purgers {    # 定義可通路來源IP;
        "127.0.0.1";
        "192.168.0.0"/24;
}
sub vcl_recv {
    if (req.method == "GET" && req.http.cookie) {    # 帶cookie首部的GET請求也緩存;
        return(hash);
    }
    if (req.url ~ "test.html") {    # test.html檔案禁止緩存;
        return(pass);
    }
    if (req.method == "PURGE") {    # PURGE請求的處理;
        if (!client.ip ~ purgers) {
            return(synth(405,"Method not allowed"));
        }
        return(hash);
    }
    if (req.http.X-Forward-For) {    # 為發往後端主機的請求添加X-Forward-For首部;
        set req.http.X-Forward-For = req.http.X-Forward-For + "," + client.ip;
    } else {
        set req.http.X-Forward-For = client.ip;
    }
    if (req.http.host ~ "(?i)^(www.)?lnmmp.com$") {    # 根據不同的通路域名,分發至不同的後端主機組;
            set req.http.host = "www.lnmmp.com";
            set req.backend_hint = web_cluster.backend();
    } elsif (req.http.host ~ "(?i)^p_w_picpaths.lnmmp.com$") {
            set req.backend_hint = img_cluster.backend();
    }
    return(hash);
}
sub vcl_hit {
    if (req.method == "PURGE") {    # PURGE請求的處理;
        purge;
        return(synth(200,"Purged"));
    }
}
sub vcl_miss {
    if (req.method == "PURGE") {    # PURGE請求的處理;
        purge;
        return(synth(404,"Not in cache"));
    }
}
sub vcl_pass {
    if (req.method == "PURGE") {    # PURGE請求的處理;
        return(synth(502,"PURGE on a passed object"));
    }
}
sub vcl_backend_response {     # 自定義緩存檔案的緩存時長,即TTL值;
        if (bereq.url ~ "\.(jpg|jpeg|gif|png)$") {
                set beresp.ttl = 7200s;
        }
        if (bereq.url ~ "\.(html|css|js)$") {
                set beresp.ttl = 1200s;
        }
    if (beresp.http.Set-Cookie) {    # 定義帶Set-Cookie首部的後端響應不緩存,直接傳回給用戶端;
        return(deliver);
    }
}
sub vcl_deliver {
    if (obj.hits > 0) {    # 為響應添加X-Cache首部,顯示緩存是否命中;
        set resp.http.X-Cache = "HIT from " + server.ip;
    } else {
        set resp.http.X-Cache = "MISS";
    }
}      

繼續閱讀