轉自:http://www.oschina.net/translate/speed-your-web-site-varnish Varnish可以有效降低web服務器的負載,提升訪問速度。根據官方的說法,Varnish是一個cache型的HTTP反向代理。 按照HTTP協議的處理過程,web服務器接受請求並且返回處理結果,理想情況下服務器要在不做額外處理的情況下,立即返回結果,但實際情況並非如此。本文將分析在web服務器處理請求的過程中,Varnish能起到什麼作用。 |
下面來看一下Varnish的安裝過程。可以從源碼進行安裝,也可以直接使用一些發行版中的預編譯包。當期Varnish的版本是3.0.3(譯者注:目前最新是2013年6月17日發佈的3.0.4版),本文將基於3.0.3進行源碼安裝。需要注意的是,2.X版的Varnish和3.X的配置文件格式發生了變化。可以從Varnish的官方網站上找到2.x到3.x升級的細節。 從源碼進行安裝經常遇到的問題是系統缺少某些依賴的文件。可以從Varnish的安裝文檔裏找到編譯所需的所有依賴的文件。 以root身份運行如下命令來下載和安裝Varnish。
|
在運行varnishd之前,需要先配置後端的web服務器,參照如下格式編輯default.vcl文件,更改爲你自己web服務器配置。
用如下命令啓動Varnishd:
執行完畢後,Varnish將進入後臺運行,同時返回命令行狀態。需要注意的是,Varnish運行時會同時啓動兩個進程,一個主進程,一個是子進程,如果子進程出現問題,主進程將重新生成一個子進程。 |
Varnishd 的啓動選項-f : 指定配置文件位置 -a : varnish監聽的本地地址和端口。 -P :PID文件位置,用來關閉Varnish -s :cache配置。默認使用256M內存 如果從一些包管理器來安裝varnish,可能安裝完畢後就會自動運行。這種情況下需要先停掉它。並使用上述命令選項來運行。否則一些配置可能和本文例子中的不同了。可以用下面的命令來檢查varnish的運行情況和配置。
|
啓動完畢之後,Varnish就可以處理並轉發請求了。在轉發過程中,varnish會儘可能的緩存結果。我們通過下面幾個簡單的GET請求,來看看varnish是如何工作的。首先,運行如下命令:
|
在這裏GET命令的選項無所謂。需要注意的是varnish返回的響應,varnish會增加三個相應頭信息,分別是“X-Varnish”、“Via”和“Age”。這些頭信息在Varnish的處理過程中非常有用。X-Varnish頭信息的後面會有一個或兩個數字,如果是一個數字,就表明varnish在緩存中沒有發現這個請求,這個數字的含義是varnish爲這個請求所做的標記ID。如果X-Varnish後是兩個數字,就表明varnish在緩存中命中了這個請求,第一個數字是請求的標識ID,第二個數字是緩存的標識ID。“Via”頭信息表明這個請求將經過一個代理。“Age”頭信息標識出這個請求將被緩存多長時間(單位:秒)。首次請求的“Age”爲0,後續的重複請求將會使Age值增大。如果後續的請求沒有是“Age”增加,那就說明varnish沒有緩存這個響應的結果。 |
現在來看看 varnishstat 命令啓動執行的情況,如下圖所示: 圖2. varnishstat 命令 最重要的是 cache_hit 和 cache_miss 這兩行。如果沒有任何命中,cache_hits 不會顯示。當越來越多的請求進來,計數器會不斷更新以反應新的命中數和未命中數。 接下來看看 varnishlog 命令: 圖3. varnishlog 命令 |
上圖顯示varnish接受請求並作出響應的內部細節,下面是varnish官方文檔中的解釋: 第一列是一個亂數,用來標識當前請求。第一列數字相同的行屬於一HTTP請求序列。第二列是日誌信息的標籤,所有的日誌信息會分類標記,以“Rx”開頭的爲接收的數據,以“Tx”開頭的爲發送的數據。第三列表示數據在客戶端,vainish和web服務器之間的傳輸狀態。第四列是被日誌記錄的數據。varnishlog命令有很多的選項可以在查詢時使用。強烈推薦在排錯或測試時使用varnishlog。可以閱讀varnish的man page來查看這個命令的詳細使用情況。下面是一些使用的例子。 |
顯示varnish和客戶端之間的通信(忽略後端web服務器):
|
現在通過上述命令,我們可以控制varnish的啓動和關閉,可以檢查緩存的命中情況,下一個問題是varnish到底緩存了什麼,會存多久? varnish的默認緩存策略是偏向保守的(可以通過配置改變)。它默認只緩存get請求和HEAD請求。不會緩存帶有Cookie和認證信息的請求,也不會緩存帶有Set-Cookie或者有變化的頭信息的響應。varnish也會檢查請求和響應中的Cache-Control頭信息,這個頭信息中會包含一些選項來控制緩存行爲。當Cache-control中Max-age的控制和默認策略衝突時,varnish不會單純的根據Cache-control信息就改變自己的緩存行爲。例如:Cache-Control: max-age=n,n爲數字,如果varnish收到web服務器的響應中包含max-age,varnish會以此值設定緩存的過期時間(單位:秒),否則varnish將會設置爲參數配置的時間,默認爲120秒。 |
注意:varnish的默認配置可以適應多數情況。例如,默認的default_ttl緩存過期時間是120秒。配置文件的詳細解釋可以閱讀varnishd的man page。如果你想改變某些默認設置,一種方式是通過命令行varnishd加上-p參數,這將重啓varnishd,並清空緩存。另外一種方式是使用varnish的管理接口,要使用varnish的管理接口需要以-T參數啓動varnish,這個參數指定了,varnish管理接口的監聽端口。然後使用varnishadm命令連接到varnish的管理接口上,然後實時的查詢varnish的運行參數,或者更改新的參數,這些都不需要重啓varnish。 想要了解詳細的情況,可以參閱varnishd、varnishadm和varnish-cli的man page。 一般情況下我們會改變varnish的緩存行爲,定製自己的緩存策略。可以將配置寫入默認的default.vcl文件,VCL是varnish的配置文件的格式,像一種簡單的腳本語言。可以通過vcl的man page來了解詳細的格式,推薦閱讀。 |
瞭解完varnish對請求和響應處理的過程之後,我們來討論一下如何改變varnish的緩存策略。varnish有一系列的子功能來完成上述處理過程,每一個子功能完成處理過程的一個部分。每一個子功能的執行結果傳遞給下一個子功能。所以我們可以通過改變子功能的返回值來改變varnish的處理動作。每一個子功能都在default.vcl中有一個默認定義,可以通過改變這些值來定製varnish的功能。
|
Varnish 子程序varnish的子程序在default.vcl中有默認的配置,如果你重新配置了某一個功能,並不意味着默認的動作一定不會執行。一般情況下,如果你重新配置了某一個功能,但是沒有返回值,varnish將會執行默認的動,因爲所有的varnish子程序都會有一個返回值。這可以看作varnish的一種容錯機制。 第一個子程序是“vcl_recv”,在姐收到完整的客戶端請求後開始執行這個子程序,可以通過修改req對象來改變默認的請求處理,varnish根據返回值決定處理方式。比如,返回:pass,將使varnish直接轉發請求到web服務器,返回:lookup,將使varnish檢查緩存。 |
下一個子程序是vcl_pass。如果在vcl_recv中返回了“pass”,將執行vcl_pass。vcl_pass有兩種返回值,pass:繼續轉發請求到後端web服務器。restart:將請求重新返回給vcl_recv。 vcl_miss和vcl_hit將會根據varnish的緩存命中情況來執行。vcl_miss的執行有兩種情況,一是從後端服務器獲取響應結果,並且在本地緩存(fetch),二是從後端獲取到響應,但本地不緩存(pass)。如果varnish緩存命中,將會執行vcl_hit,會有兩個選擇,一個直接將緩存的結果返回給客戶端(deliver),二是丟棄緩存,重新從後端服務器獲取數據(pass)。 當從後端服務器獲取到數據之後,將會執行vcl_fetch過程,在這裏可以通過beresp對象來訪問響應數據,返回兩種處理結果;deliver:按計劃將數據發送給客戶端,restart,退回這個請求。 |
在vcl_deliver子程序中,可以將響應結果發送給客戶端(deliver),結束這個請求,同時根據不同情況決定是否緩存結果。也可以返回“restart”值,來重新開始這個請求。 如前所述,通過default.vcl來控制varnish的緩存策略,通過子程序的返回值來控制varnish的動作。子程序的返回值可以基於req和resp對象,也可以基於客戶端對象,服務端對象或者後端服務器對象。但在子程序處理過程中,上述數據對象並非一直都可用。另外要注意子程序的返回值會有一定的限制範圍,一個難點就是記住在什麼時候,哪個子程序中什麼數據對象是可用的,合法的返回值有哪些。爲了讓這個問題更清晰一些,下面是一張表格,可以在修改配置的時候快速參考: |
表1. 各子程序中各數據對象的可用情況.
表2. 各子程序的合法返回值.
注意:請仔細閱讀vcl的文檔來了解相關子程序,返回值以及可用的數據對象。 下面是一些實際的例子: 修改HTTP請求的 Host 頭信息:
你可以通過 req.http.host來訪問請求的HTTP host頭信息,類似的,你可以通過req.http訪問所有的HTTP頭信息。“~”符表示相等的意思,後面是一個正則表達式. 如果匹配上,會通過set關鍵字和“=”賦值操作,將hostname改爲"example.com".修改hostname的一個作用是防止重複的緩存。 因爲varnish會通過hostname和URL來判斷緩存匹配情況,所以hostname應該被修改爲統一的形式。 下面是默認 vcl_recv 子程序的一個片段:
|
這是默認的vcl_recv配置文件的片段,在這個配置中如果發現請求不是GET或者HEAD,varnish將會直接pass這個請求,並且不會緩存這個請求的響應,如果是GET或HEAD,將會到緩存中進行查找。 匹配特定的URL,移除請求中的Cookie信息:
這個是varnish網站上的例子,如果發現請求的URL以"/images"開始,varnish會將其中的Cookie信息移除。當在cookie中設置不緩存當前響應時,varnish可以通過移除Cookie,來緩存這個響應。 |
在對圖片文件的響應頭中中移除set-Cookie:
這個是varnish網站上的另一個例子。接收到web服務器的返回結果後會觸發vcl_fetch處理過程。當從web服務器接收到新的數據後,beresp中保存web服務器返回的響應信息,req中是本次請求的信息。如果本次請求的文件是圖像文件,varnish會將響應頭信息中的“Set-Cookie”移除,並且將緩存的時間設置爲1小時(帶有Set-Cookie的響應,varnish不會緩存)。 現在我們想朝響應信息中增加一個"X-Hit"的頭信息,當緩存命中的時候,這個值爲1,未命中的時候爲0。根據上文,判斷緩存命中的最簡單的辦法是在vcl_hit中。當緩存命中是會調用vcl_hit,因此可以考慮在vcl_hit中設置這個頭信息,但從上文表1中我們看到,vcl_hit裏beresp和resp都是不可用的。一個可行的辦法就是在請求中設置裏一個臨時頭信息,然後在後續的處理過程中真正進行設置。下面我們看一下這個問題是如何解決的: |
在響應頭信息中增加“x-hit”:
上述vcl_hit和vcl_miss中設置了一個臨時的請求頭信息,來標識出緩存的命中情況。真正的處理過程在vcl_deliver裏,首先默認設置x-hit爲0,然後根據請求裏的臨時頭信息判斷緩存的命中情況,並且依此更新剛纔設置的x-hit默認值,最後刪除此臨時頭信息。之所以選擇在vcl_deliver裏來進行處理,是因爲在響應信息返回給客戶端之前,只有在vcl_deliver中纔可以訪問resp對象。 下面我們看一個針對此問題錯誤的解決辦法: |
增加“x-hit”頭信息(錯誤的方法):
在vcl_fetch裏修改服務器返回的信息(beresp),但這並不是最終發送給客戶端的數據。上述配置貌似正確,其實隱含了一個大BUG。首次請求當緩存未命中時,web服務器的響應被標上'x-hit'爲0,然後送到vcl_fetch處理,並被緩存在本地。但當後續請求緩存命中時,vcl_fetch過程不會被執行,結果就導致所有的緩存命中的響應都帶有x-hit=0的標記。這是在配置varnish時需要特別注意的一類問題。 避免這些問題的辦法是熟練掌握上述的表1的內容,並且在實際工作中多做測試。 |
下面的配置可以讓varnish將所有的數據緩存一個小時(不建議在真實環境中使用)
在上述配置中重寫了vcl_recv和vcl_fetch兩個過程。需要注意的是:如果vcl_fetch不返回“deliver”,varnish將會繼續執行默認的處理過程,就不會有想要的效果了。 一旦varnish運行生效之後,你可以通過一些負載測試工具來測試性能的改善。我通常使用的測試工具是apache自帶的ab,可以從apache的安裝包裏找到,或者通過一些第三方的包管理器進行安裝。可以在apache的網站上找到ab的使用文檔。 |
|
在下面的測試例子中,apache2.2監聽在80端口,varnish監聽在6081端口。用來做測試的頁面是一個非常簡單的perl CGI腳本,只輸出一個單行的HTML文件。通過訪問相同的URL來測試apache和varnish的不同表現。爲避免網絡因素的影響,所有的測試都在本機執行。測試中使用的ab選項非常簡單,也可以試着去改變這些選項,看看是什麼結果。 首先進行一個1000次的請求(n=1000),併發數量1個(c=1). 先測試apache:
圖 4. Apache測試結果 |
再測試varnish:
圖 5. varnish測試結果 ab的測試輸出裏包含很多信息,最基本的性能指標有“Time per request”和“Requests per second(rps)”。從上述測試結果來看,apache的每個請求在1ms以上(780rps),而varnish在0.1ms(7336rps)比apache高了10倍。 從這個測試結果說明varnish的速度比apache快,至少在當前環境下是這樣。可以嘗試改變ab的選項來進行不同的測試,例如改變併發訪問數量。 |
系統負載和磁盤IO( %iowait)系統負載是反映CPU運行狀況的指標,通常情況下系統中每個CPU核心的負載應該在1.0以下。如果你的系統中有4個CPU核心,理想的系統負載應該在4.0以下。 磁盤的%iowait反映每個CPU時間片內系統的輸入輸出(I/O)狀況。%iowait指標較高表示磁盤IO成爲了系統瓶頸會拖慢整體的速度。比如,如果收到的每個請求都需要讀取100個文件或者更多,這將會導致%iowait迅速升高然後成爲系統性能的瓶頸。 |
測試的時候除了關注系統的響應時間之外,還要看系統資源的使用情況。當測試請求持續很長時間時,我們比較一下兩者的系統資源使用情況。監控的指標就是系統的負載(system load)和磁盤IO(%iowait)。系統負載可以從top命令裏查看,%iowait可以從iostat命令看到。在測試過程中需要同時關注這兩個指標,我們打開兩個終端分別運行top和iostat。 運行iostat,2秒更新一次:
運行top:
現在就可以做測試了。因爲要看長時間持續請求的狀況下服務器的性能表現(至少要在1到10分鐘),所以需要在ab的選項里加大請求次數和併發數量。 |
使用ab對Apache進行測試:
圖 6. Apache 下系統壓力測試流量記錄 使用ab測試Varnish: ab -c 50 -n 1000000 http://localhost:6081/cgi-bin/test 圖 7. Varnish下系統壓力測試流量記錄 首先對比響應時間,雖然截圖上沒有直接顯示時間,但是我們可以從ab結束的時間計算出來,apache是23ms每次請求(2097rps),varnish是4ms每次請求(12099rps)。最大的差異是平均負載,Apache使系統的負載達到了12,使用varnish時則一直保持在0~0.4。在從apache切換到varnish做測試時,甚至要等上幾分鐘,系統負載才能回覆正常。最好在非生產環境下用比較空閒的機器做這些測試。儘管真實環境中的web服務器有很多個性化的配置和需求,但是vanish一般都可以幫助你的web服務器提高性能並且降低負載。 |