天天看點

緩存伺服器-varnish

Varnish 簡介

Varnish是一款高性能且開源的反向代理伺服器和 HTTP 加速器(其實就是帶緩存的反向代理服務),它可以把整個HTTP響應内容緩存到記憶體或檔案中,進而提高Web伺服器的響應速度。其采用全新的軟體體系機構,和現在的硬體體系緊密配合,與傳統的 squid 相比,varnish 具有性能更高、速度更快、管理更加友善等諸多優點,很多大型的網站都開始嘗試使用 varnish 來替換 squid,這些都促進 varnish 迅速發展起來。

Varnish内置強大的VCL(Varnish Configuration Language)配置語言,允許通過各種條件判斷來靈活調整緩存政策。在程式啟動時,varnish就把VCL轉換成二進制代碼,是以性能非常高。

挪威的最大的線上報紙 Verdens Gang(vg.no) 使用 3 台 Varnish 代替了原來的 12 台 Squid,性能比以前更好,這是 Varnish 最成功的應用案例。

Varnish特點:

(1)是基于記憶體緩存,重新開機後資料将消失。

(2)利用虛拟記憶體方式,io性能好。

(3)支援設定0~60秒内的精确緩存時間。

(4)VCL配置管理比較靈活。

(5)32位機器上緩存檔案大小為最大2G。

(6)具有強大的管理功能,例如top,stat,admin,list等。

(7)狀态機設計巧妙,結構清晰。

(8)利用二叉堆管理緩存檔案,達到積極删除目的。

Varnish與Squid的對比

說到Varnish,不能不提Squid,Squid是一個高性能的代理緩存伺服器,它和varnish之間有諸多的異同點,這裡分析如下:

兩者相同點:

(1)都是一個反向代理伺服器,

(2)都是開源軟體,

Varnish的優點:

(1)Varnish的穩定性很高,兩者在完成相同負荷的工作時,Squid伺服器發生故障的幾率要高于Varnish,因為使用Squid要經常重新開機。

(2)Varnish通路速度更快,Varnish采用了“Visual Page Cache”技術,所有緩存資料都直接從記憶體讀取,而squid是從硬碟讀取,因而Varnish在通路速度方面會更快。

(3)Varnish可以支援更多的并發連接配接,因為Varnish的TCP連接配接釋放要比Squid快。因而在高并發連接配接情況下可以支援更多TCP連接配接。

(4)Varnish可以通過管理端口,使用正規表達式批量的清除部分緩存,而Squid是做不到的。

與傳統的Squid相比,Varnish的缺點:

(1)varnish在高并發狀态下CPU、IO、記憶體等資源開銷都高于Squid。

(2)varnish程序一旦Hang、Crash或者重新開機,緩存資料都會從記憶體中完全釋放,此時所有請求都會發送到後端伺服器,在高并發情況下,會給後端伺服器造成很大壓力。

Varnish 的後端存儲

Varnish支援多種不同類型的後端存儲,這可以在varnishd啟動時使用-s選項指定。後端存儲的類型包括:

file:使用特定的檔案存儲全部的緩存資料,并通過作業系統的mmap()系統調用将整個緩存檔案映射至記憶體區域(如果條件允許);

malloc:使用malloc()庫調用在varnish啟動時向作業系統申請指定大小的記憶體空間以存儲緩存對象;

persistent(experimental):與file的功能相同,但可以持久存儲資料(即重新開機varnish資料時不會被清除);仍處于測試期;

varnish系統架構

緩存伺服器-varnish

arnish主要運作兩個程序:Management程序和Child程序(也叫Cache程序)。如上圖,

Management程序

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

Management 管理接口:

CLI interface 指令行接口

Telnet interface telnet接口

Web interface Web管理接口

Child/Cache 程序

Child/Cache 程序包含多種類型的線程,常見的如:

Accept 線程:接收新的連接配接請求并響應;

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

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

Commad line 線程 : 管理接口

Storage/hashing 線程 :緩存存儲

Log/stats 線程:日志管理線程

Backend Communication 線程:管理後端主機線程

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

Varnish 緩存的工作流程

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

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

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

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

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

Varnish Configuration Language - VCL(varnish配置語言-VCL)

Varnish有一個很棒的配置系統,大部分其他的系統使用配置指令,讓您打開或者關閉一些開關。Varnish使用區域配置語言,這種語言叫做“VCL”(varnish configuration language),在執行vcl時,varnish就把VCL轉換成二進制代碼。

VCL檔案被分為多個子程式,不同的子程式在不同的時間裡執行,比如一個子程式在接到請求時執行,另一個子程式在接收到後端伺服器傳送的檔案時執行。   

varnish将在不同階段執行它的子程式代碼,因為它的代碼是一行一行執行的,不存在優先級問題。随時可以調用這個子程式中的功能并且當他執行完成後就退出。

   如果到最後您也沒有調用您的子程序中的功能,varnish将執行一些内建的VCL代碼,這些代碼就是default.vcl中被注釋的代碼。

   99%的幾率您需要改變vcl_recv 和 vcl_fetch這兩個子程序。

Varnish 狀态引擎(state engine)

VCL用于讓管理者定義緩存政策,而定義好的政策将由varnish的management程序分析、轉換成C代碼、編譯成二進制程式并連接配接至child程序。varnish内部有幾個所謂的狀态(state),在這些狀态上可以附加通過VCL定義的政策以完成相應的緩存處理機制,是以VCL也經常被稱作“域專用”語言或狀态引擎,“域專用”指的是有些資料僅出現于特定的狀态中。

1.VCL狀态引擎

在VCL狀态引擎中,狀态之間具有相關性,但彼此間互相隔離,每個引擎使用return(x)來退出目前狀态并訓示varnish進入下一個狀态。

Varnish開始處理一個請求時,首先需要分析HTTP請求本身,比如從首部擷取請求方法、驗正其是否為一個合法的HTT請求等。當這些基本分析結束後就需要做出第一個決策,即varnish是否從緩存中查找請求的資源。這個決定的實作則需要由VCL來完成,簡單來說,要由vcl_recv方法來完成。如果管理者沒有自定義vcl_recv函數,varnish将會執行預設的vcl_recv函數。然而,即便管理者自定義了vcl_recv,但如果沒有為自定義的vcl_recv函數指定其終止操作(terminating),其仍将執行預設的vcl_recv函數。事實上,varnish官方強烈建議讓varnish執行預設的vcl_recv以便處理自定義vcl_recv函數中的可能出現的漏洞。

HTTP請求基本處理流程

緩存伺服器-varnish

Varnish處理HTTP請求的過程大緻分為如下幾個步驟:

Receive 狀态(vcl_recv):也就是請求處理的入口狀态,根據VCL規則判斷該請求應該pass(vcl_pass)或是 pipe(vcl_pipe),還是進入 lookup(本地查詢);

Lookup 狀态:進入該狀态後,會在 hash 表中查找資料,若找到,則進入 hit(vcl_hit)狀态,否則進入 miss(vcl_miss)狀态;

Pass(vcl_pass)狀态:在此狀态下,會直接進入後端請求,即進入 fetch(vcl_fetch)狀态;

Fetch(vcl_fetch)狀态:在 fetch 狀态下,對請求進行後端擷取,發送請求,獲得資料,并根據設定進行本地存儲;

Deliver(vcl_deliver)狀态:将擷取到的資料發給用戶端,然後完成本次請求;

varnish内置函數

vcl_recv:用于接收和處理請求;當請求到達并成功接收後被調用,通過判斷請求的資料來決定如何處理請求;

vcl_pipe:此函數在進入pipe模式時被調用,用于将請求直接傳遞至後端主機,并将後端響應原樣傳回用戶端;

vcl_pass:此函數在進入pass模式時被調用,用于将請求直接傳遞至後端主機,但後端主機的響應并不緩存直接傳回用戶端;

vcl_hit:在執行 lookup 指令後,在緩存中找到請求的内容後将自動調用該函數;

vcl_miss:在執行 lookup 指令後,在緩存中沒有找到請求的内容時自動調用該方法,此函數可用于判斷是否需要從後端伺服器擷取内容;

vcl_hash:在vcl_recv調用後為請求建立一個hash值時,調用此函數;此hash值将作為varnish中搜尋緩存對象的key;

vcl_purge:pruge操作執行後調用此函數,可用于建構一個響應;

vcl_deliver:将在緩存中找到請求的内容發送給用戶端前調用此方法;

vcl_backend_fetch:向後端主機發送請求前,調用此函數,可修改發往後端的請求;

vcl_backend_response:獲得後端主機的響應後,可調用此函數;

vcl_backend_error:當從後端主機擷取源檔案失敗時,調用此函數;

vcl_init:VCL加載時調用此函數,經常用于初始化varnish子產品(VMODs)

vcl_fini:當所有請求都離開目前VCL,且目前VCL被棄用時,調用此函數,經常用于清理varnish子產品;

Varnish内置公用變量:

VCL内置的公用變量可以用在不同的VCL函數中,根據這些公用變量使用的不同階段。

緩存伺服器-varnish

内置變量的分類(如上圖):

req:請求到達時可用的變量

bereq:向後端主機請求時可用的變量

beresp:從後端主機擷取内容時可用的變量

resp:對用戶端響應時可用的變量

obj:存儲在記憶體中時對象屬性相關的可用的變量

Varnish安裝與配置及相關執行個體

一、環境準備:

系統環境

Centos6.5(2.6.32-431.el6.x86_64) 關閉防火牆和Selinux

IP規劃:

node:eth0:172.16.19.20/16

node1:eth0: 172.16.19.21/16

node2:eth0: 172.16.19.22/16

主機hosts檔案

# cat /etc/hosts
127.0.0.1   localhost localhost.localdomain localhost4 localhost4.localdomain4
::1         localhost localhost.localdomain localhost6 localhost6.localdomain6
172.16.19.21    node1.luo.com    node1  
172.16.19.22    node2.luo.com    node2
172.16.19.23    node3.luo.com    node3 
      

時間同步

# ntpdate 172.16.0.1

每隔10分鐘同步時間crontab -e 編輯crontab工作内容如下:

*/10 * * * * /usr/sbin/ntpdate 172.16.0.1 &>/dev/null&&/sbin/hwclock-w

# service crond restart

二、後端(WEB)伺服器配置

分别為node1與node2安裝httpd并寫好測試頁面

# yum install httpd

node1測試頁面

# echo "<h1>node1.luo.com</h1>" > /var/www/html/index.html
# echo "<h1>test.com</h1>" >  /var/www/html/test.html
# echo "<h1>node1 is alived.....................</h1>" > /var/www/html/.health.html
# service httpd restart
# curl http://172.16.19.21
      

node2測試頁面

#  echo "<h1>node2.luo.com</h1>" > /var/www/html/index.html

# echo "<h1>node2 is alived.....................</h1>"  > /var/www/html/.health.html

# service httpd restart

# curl http://172.16.19.22

三、varnish伺服器配置

軟體下載下傳與安裝

varnish下載下傳位址:

https://repo.varnish-cache.org/redhat/

https://www.varnish-cache.org/releases

本文使用varnish-3.0.5版本進行示範,最新版本為varnish-4.0.1,在此使用rpm方式安裝

yum安裝已下載下傳的rpm包可解決依賴關系:

# yum install -y varnish-3.0.5-1.el6.x86_64.rpm \

varnish-libs-3.0.5-1.el6.x86_64.rpm \

varnish-docs-3.0.5-1.el6.x86_64.rpm

主要配置檔案

/etc/rc.d/init.d/varnish # 啟動服務腳本
/etc/sysconfig/varnish # 啟動腳本的配置檔案
/etc/varnish #全局配置檔案
/etc/varnish/default.vcl #預設VCL
/var/log/varnish #日志檔案
      

初次啟動varnish

a、修改啟動腳本的配置檔案

# vim /etc/sysconfig/varnish

VARNISH_LISTEN_PORT=80 #這裡設定的監聽端口設定為6081
VARNISH_ADMIN_LISTEN_ADDRESS=127.0.0.1 #遠端管理IP
VARNISH_ADMIN_LISTEN_PORT=6082 #管理端口
VARNISH_STORAGE="malloc,64M" #我們這裡設定的是64M,因為虛拟機記憶體小,在生産環境中可以根據實際需求設定
#VARNISH_STORAGE="file,${VARNISH_STORAGE_FILE},${VARNISH_STORAGE_SIZE}"
      

b、啟動服務,檢視監聽端口80與6082

# service varnish start

# ss -tnlp |grep varnish

LISTEN     0      128                      :::80                    :::*      users:(("varnishd",9563,8))

LISTEN     0      128                       *:80                     *:*      users:(("varnishd",9563,7))

LISTEN     0      10                127.0.0.1:6082                     *:*      users:(("varnishd",9562,6))

配置檔案詳解

# cat /etc/sysconfig/varnish
# Configuration file for varnish
#
# /etc/init.d/varnish expects the variable $DAEMON_OPTS to be set from this
# shell script fragment.
#
# Maximum number of open files (for ulimit -n)
NFILES=131072 #打開最大檔案數
# Locked shared memory (for ulimit -l)
# Default log size is 82MB + header
MEMLOCK=82000 #預設日志大小
# Maximum number of threads (for ulimit -u)
NPROCS="unlimited" #最大線程數
# Maximum size of corefile (for ulimit -c). Default in Fedora is 0
# DAEMON_COREFILE_LIMIT="unlimited" #最大核心打開的檔案數
# Set this to 1 to make init script reload try to switch vcl without restart.
# To make this work, you need to set the following variables
# explicit: VARNISH_VCL_CONF, VARNISH_ADMIN_LISTEN_ADDRESS,
# VARNISH_ADMIN_LISTEN_PORT, VARNISH_SECRET_FILE, or in short,
# use Alternative 3, Advanced configuration, below
RELOAD_VCL=1 #是否自動加載VCL
# This file contains 4 alternatives, please use only one.
## Alternative 1, Minimal configuration, no VCL #方案1,最小配置,不友善
#
# Listen on port 6081, administration on localhost:6082, and forward to
# content server on localhost:8080. Use a fixed-size cache file.
#
#DAEMON_OPTS="-a :6081 \
#       -T localhost:6082 \
#       -b localhost:8080 \
#       -u varnish -g varnish \
#       -s file,/var/lib/varnish/varnish_storage.bin,1G"
## Alternative 2, Configuration with VCL #方案2,配置元件
#
# Listen on port 6081, administration on localhost:6082, and forward to
# one content server selected by the vcl file, based on the request. Use a
# fixed-size cache file.
#
#DAEMON_OPTS="-a :6081 \
#       -T localhost:6082 \
#       -f /etc/varnish/default.vcl \
#       -u varnish -g varnish \
#       -S /etc/varnish/secret \
#       -s file,/var/lib/varnish/varnish_storage.bin,1G"
## Alternative 3, Advanced configuration #方案3,進階配置
#
# See varnishd(1) for more information.
#
# # Main configuration file. You probably want to change it :)
VARNISH_VCL_CONF=/etc/varnish/default.vcl #預設的VCL存放位置
#
# # Default address and port to bind to
# # Blank address means all IPv4 and IPv6 interfaces, otherwise specify
# # a host name, an IPv4 dotted quad, or an IPv6 address in brackets.
# VARNISH_LISTEN_ADDRESS=
VARNISH_LISTEN_PORT=6081 #服務監聽端口
#
# # Telnet admin interface listen address and port
VARNISH_ADMIN_LISTEN_ADDRESS=127.0.0.1 #管理IP
VARNISH_ADMIN_LISTEN_PORT=6082 #管理端口
#
# # Shared secret file for admin interface
VARNISH_SECRET_FILE=/etc/varnish/secret #預設的加密檔案
#
# # The minimum number of worker threads to start
VARNISH_MIN_THREADS=50 #最小線程數
#
# # The Maximum number of worker threads to start
VARNISH_MAX_THREADS=1000 #最大線程數
#
# # Idle timeout for worker threads
VARNISH_THREAD_TIMEOUT=120 #線程逾時時間
#
# # Cache file location
VARNISH_STORAGE_FILE=/var/lib/varnish/varnish_storage.bin #緩存檔案的位置
#
# # Cache file size: in bytes, optionally using k / M / G / T suffix,
# # or in percentage of available disk space using the % suffix.
VARNISH_STORAGE_SIZE=1G #設定存儲的大小
#
# # Backend storage specification #後端存儲規範,這裡是我們主要配置的地方
VARNISH_STORAGE="file,${VARNISH_STORAGE_FILE},${VARNISH_STORAGE_SIZE}" #預設存儲在檔案裡,我們這裡修改為malloc
#
# # Default TTL used when the backend does not specify one #預設TTL時使用的後端不指定一個
VARNISH_TTL=120
#
# # DAEMON_OPTS is used by the init script. If you add or remove options, make
# # sure you update this section, too. #所有的啟動選項
DAEMON_OPTS="-a ${VARNISH_LISTEN_ADDRESS}:${VARNISH_LISTEN_PORT} \
       -f ${VARNISH_VCL_CONF} \
       -T ${VARNISH_ADMIN_LISTEN_ADDRESS}:${VARNISH_ADMIN_LISTEN_PORT} \
       -t ${VARNISH_TTL} \
       -w ${VARNISH_MIN_THREADS},${VARNISH_MAX_THREADS},${VARNISH_THREAD_TIMEOUT} \
       -u varnish -g varnish \
       -S ${VARNISH_SECRET_FILE} \
       -s ${VARNISH_STORAGE}"
#
## Alternative 4, Do It Yourself. See varnishd(1) for more information. #方案4,設定你自己的配置。看到varnishd(1)更多的資訊。
#
# DAEMON_OPTS=""
      

# cat /etc/varnish/test.vcl

#配置後端伺服器
backend web1 {
  .host = "172.16.19.21";
  .port = "80";
}
 sub vcl_recv {
 set req.backend = web1;
}
#設定條件讓請求test.html檔案時不緩存
 sub vcl_recv {
     if (req.url ~ "^/test.html$") {
        return(pass);
     }
     set req.backend = web1;
     }

#修改vcl_deliver增一個響應頭部
 sub vcl_deliver {
    if (obj.hits > 0) {
       set resp.http.X-Cache = "Hit from"+" "+server.ip;
    } else {
       set resp.http.X-Cache = "Miss via"+" "+server.ip;
    }
}
      

# varnishadm

varnish> vcl.load a1 ./test1.vcl
200        
VCL compiled.
varnish> vcl.use a1
200        

varnish> vcl.list
200        
available       0 boot
active          1 a1

varnish> vcl.show a1
200        
backend web1 {
  .host = "172.16.19.21";
  .port = "80";
}
.............


      

首頁面可以緩存命中。通路網頁第一的時候是X-Cache:Miss from 172.16.19.20,第二次重新整理就可以命中緩存,如下圖

緩存伺服器-varnish

配置test.html檔案不可以緩存,測試結果

緩存伺服器-varnish

無論你通路多少次(ctrl+F5強制重新整理),緩存都不會命中的。X-Cache: Miss via 172.16.19.20

執行個體:

# 建立後端主機
backend web1 {
    .host = "172.16.19.21";
    .port = "80";
    .probe = {
       .url = "/.health.html";
       .window = 5;
       .threshold = 2;
       .interval = 3s;
       .timeout = 2s;
     } 
}

backend web2 {
    .host = "172.16.19.22";
    .port = "80";
    .probe = {
       .url = "/.health.html";
       .window = 5;
       .threshold = 2;
       .interval = 3s;
       .timeout = 2s;
     }
}
# 建立後端負載均衡主機組,即directors,random算法;
 director webgroup random {        
    { 
       .backend = web1;
       .weight = 1; 
    }
    {  .backend = web2;
       .weight = 1;
    }
}

acl purgers {    # 定義重新整理緩存的來源IP;
        "127.0.0.1";
        "192.168.1.0"/24;
        "172.16.0.0"/16;
}

sub vcl_recv {
  if (req.request == "PURGE") {
  if (!client.ip ~ purgers) {
    error 405 "Method not allowed";
  }
  return (lookup);
  }
  set req.backend = webgroup;
}
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";
  }
}

sub vcl_deliver {
    if (obj.hits > 0) {    # 為響應添加X-Cache首部,顯示緩存是否命中;
        set resp.http.X-Cache = "HIT " + server.ip;
    } else {
        set resp.http.X-Cache = "MISS " + server.ip;
    }
}


      

通過 varnishadm 手動清除緩存

varnish> ban.url  .*$ #清除所有

varnish> ban.url  /index.html #清除index.html頁面緩存

varnish> ban.url /admin/$ #清除admin目錄緩存

繼續閱讀