Linux 緩存加速 Varnish

Varnish是一款高性能的開源HTTP加速器挪威最大的在線報紙 Verdens Gang 使用3臺Varnish代替了原來的12臺Squid,性能比以前更好。作者Poul-Henning Kamp是FreeBSD的內核開發者之一,他認爲現在的計算機比起1975年已經複雜許多。在1975年時,儲存媒介只有兩種:內存與硬盤。但現在計算機系統的內存除了主存外,還包括了CPU內的L1、L2,甚至有L3快取。硬盤上也有自己的快取裝置,因此Squid Cache自行處理物件替換的架構不可能得知這些情況而做到最佳化,但操作系統可以得知這些情況,所以這部份的工作應該交給操作系統處理,這就是 Varnish cache設計架構。

Varnish項目是2006年發佈的第一個版本0.9.距今已經有十多年了,此文檔之前也提過varnish還不穩定,那是2007年時候編寫的,經過varnish開發團隊和網友們的辛苦耕耘,現在的varnish已經很健壯。很多門戶網站已經部署了varnish,並且反應都很好,甚至反應比squid還穩定,且效率更高,資源佔用更少。相信在反向代理,web加速方面,varnish已經有足夠能力代替squid。

Varnish與一般服務器軟件類似,分爲master(management)進程和child(worker,主要做cache的工作)進程。master進程讀入命令,進行一些初始化,然後fork並監控child進程。child進程分配若干線程進行工作,主要包括一些管理線程和很多woker線程。

針對文件緩存部分,master讀入存儲配置(-s file[,path[,size[,granularity]]] ),調用合適的存儲類型,然後創建/讀入相應大小的緩存大文件。接着,master初始化管理該存儲空間的結構體。這些變量都是全局變量,在fork以後會被child進程所繼承(包括文件描述符)。

在child進程主線程初始化過程中,將前面打開的存儲大文件整個mmap到內存中(如果超出系統的虛擬內存,mmap失敗,進程會減少原來的配置mmap大小,然後繼續mmap),此時創建並初始化空閒存儲結構體,掛到存儲管理結構體,以待分配。

接着,真正的工作開始,Varnish的某個負責接受新HTTP連接的線程開始等待用戶,如果有新的HTTP連接過來,它總負責接收,然後叫醒某個等待中的線程,並把具體的處理過程交給它。Worker線程讀入HTTP請求的URI,查找已有的object,如果命中則直接返回並回複用戶。如果沒有命中,則需要將所請求的內容,從後端服務器中取過來,存到緩存中,然後再回復。

分配緩存的過程是這樣的:它根據所讀到object的大小,創建相應大小的緩存文件。爲了讀寫方便,程序會把每個object的大小變爲最接近其大小的內存頁面倍數。然後從現有的空閒存儲結構體中查找,找到最合適的大小的空閒存儲塊,分配給它。如果空閒塊沒有用完,就把多餘的內存另外組成一個空閒存儲塊,掛到管理結構體上。如果緩存已滿,就根據LRU機制,把最舊的object釋放掉。

釋放緩存的過程是這樣的:有一個超時線程,檢測緩存中所有object的生存期,如果超初設定的TTL(Time To Live)沒有被訪問,就刪除之,並且釋放相應的結構體及存儲內存。注意釋放時會檢查該存儲內存塊前面或後面的空閒內存塊,如果前面或後面的空閒內存和該釋放內存是連續的,就將它們合併成更大一塊內存。

整個文件緩存的管理,沒有考慮文件與內存的關係,實際上是將所有的object都考慮是在內存中,如果系統內存不足,系統會自動將其換到swap空間,而不需要varnish程序去控制。

Varnish特點與Squid的對比

Varnish特點:

  基於內存緩存,重啓後數據將消失。 
  利用虛擬內存方式,I/O性能好。 
  支持設置0~60秒內的精確緩存時間。 
  VCL(全稱varnish config language,這是Varnish自己領域的特定語言)配置管理比較靈活。 
  32位機器上緩存文件大小爲最大2G。 
  具有強大的管理功能,例如top,stat,admin,list等。 
  狀態機設計巧妙,結構清晰。 
  利用二叉堆管理緩存文件,達到積極刪除目的。

Varnish與Squid的對比:

 相同點:

  都是一個反向代理服務器;
  都是開源軟件;

 Varnish相較於Squid的優點:

  Varnish的穩定性很高,兩者在完成相同負荷的工作時,Squid服務器發生故障的機率要高於Varnish,因爲使用Squid要經常重啓;
  Varnish訪問速度更快,Varnish採用了“Visual Page Cache”技術,所有緩存數據都直接從內存讀取,而Squid是從硬盤讀取,因此Varnish在訪問速度方面會更快;
  Varnish可以支持更多的併發連接,因爲Varnish的TCP連接釋放要比Squid快,所以在高併發連接情況下可以支持更多TCP連接;
  Varnish可以通過管理端口,使用正則表達式批量的清除部分緩存,而Squid是做不到的;
  Squid屬於是單進程使用單核CPU,但Varnish是通過fork形式打開多進程來做處理,所以是合理的使用所有核來處理相應的請求;

 Varnish相較於Squid的缺點:

  Varnish在高併發狀態下CPU、I/O和內存等資源開銷都高於Squid;
  Varnish進程一旦Hang(掛起)、Crash(崩潰)或者重啓,緩存數據都會從內存中完全釋放,此時所有請求都會發送到後端服務器,在高併發情況下,會給後端服務器造成很大壓力;
  在Varnish使用中如果單個url的請求通過HA/F5(負載均衡)每次請求不同的varnish服務器中,被請求varnish服務器都會被穿透到後端,而且同樣的請求會在多臺服務器上緩存,也會造成Varnish的緩存的資源浪費,也會造成性能下降。


實驗環境

  • 192.168.1.2 CentOS 7 Varnish
  • Web1 192.168.1.3 CentOS 7 Nginx+PHP

  • Web2 192.168.1.4 CentOS 7 Nginx+PHP

  • Web3 192.168.1.5 CentOS 7 Nginx+PHP

  • Varnish官網地址:http://varnish-cache.org/

  • Yum 安裝方式,https://packagecloud.io/varnishcache

  • yum -y install varnish

  • varnishd -V 查看版本信息

  • /etc/varnish              #varnish工作目錄
    /etc/varnish/default.vcl    #默認配置各Child/Cache線程的緩存策略
    /usr/bin/varnishadm     #varnish內置的管理工具,端口默認爲 6082
    /usr/bin/varnishhist      #Shared Memory Log交互工具
    /usr/bin/varnishlog
    /usr/bin/varnishncsa
    /usr/bin/varnishstat
    /usr/bin/varnishtop
    /usr/bin/varnishtest    #內置測試工具程序
    /usr/lib/systemd/system/varnish.service  #varnish服務啓動腳本
    /usr/lib/systemd/system/varnishncsa.service  #日誌持久服務啓動腳本

  • /usr/sbin/varnishreload    #VCL配置文件重載程序

  • varnish 主配置文件 /etc/varnish/default.vcl  varnish使用了一種叫VCL的語言去對其進行配置
  • vcl_recv 函數: 接收和處理請求,當請求到達併成功接收後被調用,判斷請求的數據並決定如何處理請求;

          pass 表示進入pass模式,將請求交給vcl_pass 函數

        pipe 表示進入pipe模式,將請求交個vcl_pipe函數

        error code[reason]  表示返回'code'給客戶端並放棄請求, 'code'是錯誤標示例如200 405等,'reason'是錯誤原因

  • vcl_pipe函數:該函數在進入pipe模式時被調用,用於將請求直接傳遞給後端主機,在請求和返回內容沒有改變的情況下,將不變的內容返回給客戶端,直到這個鏈接關閉;該函數有這麼些返回值=>

        error code[reason]  同vcl_recv

        pipe 

  • vcl_pass函數: 該函數進入pass模式被調用,用於直接將請求發送給後端主機,後端主機響應後發送給客戶端,不進行任何緩存,每次都返回最新內容,該函數有幾個返回值=>

        error code[reason] 同vcl_recv

        pass

  • lookup函數: 表示在緩存中查找到被請求的對象,並且根據查找的結果交給vcl_hit函數(命中)或vcl_miss(未命中)函數處理
  • vcl_hit函數:在執行lookup後,如果在緩存中找到對象,該函數將會被自動調用,該函數有以下幾個返回值 =>

      deliver 表示找到內容發送給客戶端,把控制權交給vcl_deliver函數

      error code[reason] 同vcl_recv

      pass 

  • vcl_miss函數: 在執行lookup後,在緩存中沒有找到對象,該函數被調用,該函數可用於判斷是否從後端請求內容,該函數有以下幾個返回值 =>

      fetch 表示從後端獲取內容,並把控制權交給vcl_fetch函數

      error code[reason]  同vcl_recv

      pass

  • vcl_fetch函數:從後端主機更新緩存獲取內容後調用該函數,接着判斷獲取的內容是放入緩存還是直接給客戶端,該函數有以下幾個返回值=》

      error code[reason]  同vcl_recv

      pass 

      deliver

  • vcl_deliver函數:將在緩存中找到的內容發送給客戶端調用的方法,該函數有以下幾個返回值=》

      error code[reason]  同vcl_recv

      deliver

  • vcl_timeout函數:在緩存內容到期前調用該函數,該函數有以下幾個返回值=》

      discard 表示中緩存中清除該內容

      fetch

  • vcl_discard函數:緩存內容到期後或者緩存空間不夠時自動被調用,該函數有以下幾個返回值=》

      keep 表示在內容中保留該緩存

      discard

  • VCL處理流程可以用下面這張圖表示

Receive狀態:請求處理的入口狀態,根據VCL規則判斷該請求應該在Pass或Pipe,還是進入Lookup(到本地緩存中查詢)

Lookup狀態:進入此狀態後,會在hash表中查詢數據,如果找到則進入Hit狀態,否則進入Miss狀態

Pass狀態:在此狀態下會進入後端獲取內容,既進入Fetch狀態

Fetch狀態:從後端獲取請求,發送請求,獲得數據,並存儲到本地

Deliver狀態:將獲取的數據發送給客戶端,然後完成本次請求

  • VCL裏面還有些變量,可以用在VCL函數中,用於邏輯處理

請求到達後可以使用的內置公用變量:

req.backend    =>    指定對應的後端主機

server.ip      =>    表示服務器端IP

client.ip      =>    表示客戶端IP

req.request    =>    指定請求的類型,如GET POST PUT DELETE

req.url      =>    知道請求的URL

req.proto      =>    表示客戶端發起請求的HTTP協議版本

req.http.header  =>    表示請求中的頭部信息

req.restarts    =>    表示請求重啓的次數,默認最大值爲4

  • VCL向後端主機請求時使用的內置變量

beresp.request   =>    指定請求的類型,如GET POST 

beresp.url      =>    指定請求的地址

beresp.proto    =>    指定請求的協議版本號

beresp.http.header =>    對應請求的HTTP頭信息

beresp.ttl      =>    表示緩存的週期,單位秒

  • 從cache或後端主機獲取內容後可以使用的內置變量

obj.status     =>    表示返回的狀態碼,如200 404 500 等

obj.cacheable   =>    表示返回的內容是否可以緩存

obj.valid     =>    表示是否是有效的HTTP應答

obj.response   =>    表示應答的狀態信息

obj.ttl      =>    表示返回內容的生存週期,單位秒

obj.lastuse    =>    表示上次請求到現在的間隔,單位秒

  • 客戶端應答時可以使用的變量

resp.status    =>    表示返回給客戶端的狀態碼

resp.proto    =>    表示返回給客戶端的HTTP協議版本

resp.http.header  =>        表示返回給客戶端的頭信息

resp.response   =>    表示返回給客戶端的狀態信息

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

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

.probe中的探測指令常用的有:
   (1) .url:探測後端主機健康狀態時請求的URL,默認爲“/”;
   (2) .request: 探測後端主機健康狀態時所請求內容的詳細格式,定義後,它會替換.url指定的探測方式;比如:
   .request =
       "GET /.healthtest.html HTTP/1.1"
       "Host: www.myweb.com"
       "Connection: close";
   (3) .window:設定在判定後端主機健康狀態時基於最近多少次的探測進行,默認是8;
   (4) .threshold:在.window中指定的次數中,至少有多少次是成功的才判定後端主機正健康運行;默認是3;
   (5) .initial:Varnish啓動時對後端主機至少需要多少次的成功探測,默認同.threshold;
   (6) .expected_response:期望後端主機響應的狀態碼,默認爲200;
   (7) .interval:探測請求的發送週期,默認爲5秒;
   (8) .timeout:每次探測請求的過期時長,默認爲2秒;


  • 設置後臺健康狀態檢查

  • vim /etc/varnish/health_check.vcl

  • probe chk {
        .interval = 5s;     # 每隔1秒鐘嘗試一次
        .timeout = 3s;    #每次探測請求的過期時長
        .window = 10;   # 最多嘗試10次,判斷採樣的樣本
        .threshold = 8;  # 如果採樣10次,如果有8次是錯誤的,就認爲是失敗的,上線也是一樣
     
        .request = 
        "GET /.test.html HTTP/1.1"   #測試根目錄下的 .test.html ,這個文件必須存在且能夠訪問
        "Host: www.myweb.com"
        "Connection: close"
        "Accept-Encoding: foo/bar";
    }

  • 後端服務器地址池配置
    vim /etc/varnish/backends.vcl

  • import directors;
    include "health_check.vcl";

    backend web1 {
        .host = "192.168.1.3";
        .port = "80";
        .probe = chk;
    }

    backend web2 {
        .host = "192.168.1.4";
        .port = "80";
        .probe = chk;
    }

    backend web3 {
        .host = "192.168.1.5";
        .port = "80";
        .probe = chk;
    }
    ## 負載均衡池
    sub vcl_init {
        new web = directors.round_robin();  #以輪詢方式調度
        web.add_backend(web1);
        web.add_backend(web2);
        web.add_backend(web3);
    }

  • 緩存規則主配置
    # vim /etc/varnish/default.vcl

  • vcl 4.1;
    import std;
    include "backends.vcl";
    acl purge_ip {   # 定義acl訪問控制,只允許以下網段或主機執行purgers操作
        "127.0.0.1";
        "192.168.0.0"/16;
    }
    sub vcl_recv {
        if (req.method == "PURGE"){  # 如果請求方法是PURGE,並且客戶端IP在上面定義的網段內的就允許執行PURGE操作,否則生成一個錯誤頁面返回給用戶
                if (!client.ip ~ purge_ip) {
                return(synth(405,"Purging not allowed for "+client.ip));
                }
        return(purge);    
        }
        if (req.http.host ~ "^(www.)?myweb.com") {
        set req.http.host = "www.myweb.com";
        set req.backend_hint = web.backend();
        }
    }

    sub vcl_purge{
        return (synth(200,"success"));
    }
    sub vcl_deliver {
        if (obj.hits > 0) {
            set resp.http.X-Cache = "HIT cache" + " " + server.ip;
        }
        else {
            set resp.http.X-Cache = "Miss cache";
        }
    }

    sub vcl_pass {
        if (req.method == "PURGE") {
            return (synth(502, "PURGE on a passed object."));
        }
    }

  • 爲了方便測試,將 varnish 默認啓動端口 6081 更改爲 80

  • 編輯 vim /usr/lib/systemd/system/varnish.service

  • [Unit]
    Description=Varnish Cache, a high-performance HTTP accelerator
    After=network-online.target

    [Service]
    Type=forking
    KillMode=process

    # Maximum number of open files (for ulimit -n)
    LimitNOFILE=131072

    # Locked shared memory - should suffice to lock the shared memory log
    # (varnishd -l argument)
    # Default log size is 80MB vsl + 1M vsm + header -> 82MB
    # unit is bytes
    LimitMEMLOCK=85983232

    # Enable this to avoid "fork failed" on reload.
    TasksMax=infinity

    # Maximum size of the corefile.
    LimitCORE=infinity

    #ExecStart=/usr/sbin/varnishd -a :6081 -f /etc/varnish/default.vcl -s malloc,256m
    ExecStart=/usr/sbin/varnishd -a :80 -f /etc/varnish/default.vcl -s malloc,256m  #修改默認監聽端口爲 :80
    ExecReload=/usr/sbin/varnishreload

    [Install]
    WantedBy=multi-user.target

  • systemctl daemon-reload

  • systemctl restart varnish

  • 使用 varnishadm 進行後端健康檢查
  • backend.list 
    200        
    Backend name   Admin    Probe    Health     Last change
    boot.web1      probe    10/10    healthy    Sat, 28 Mar 2020 08:03:18 GMT
    boot.web2      probe    10/10    healthy    Sat, 28 Mar 2020 08:01:58 GMT
    boot.web3      probe    10/10    healthy    Sat, 28 Mar 2020 08:01:58 GMT
    boot.web       probe    3/3      healthy    Sat, 28 Mar 2020 08:03:18 GMT

  • 編輯 /etc/hosts 添加 192.168.1.2 www.myweb.com
  • curl www.myweb.com
  • X-Cache: HIT cache192.168.1.2

  • 清除緩存  curl -X PURGE www.myweb.com
  • curl www.myweb.com

  • 將其中一臺的 web 服務停掉
  • systemctl stop nginx

  • web2 的狀態爲 sick 不可用
  • 重新啓動 web2 的nginx

 

 

 

 

 

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