Varnish進程及存儲
Varnish進程結構
Varnish管理進程
varnishd啓動之後有兩個進程:管理進程(父進程ID爲1)和子進程(父進程ID爲管理進程ID).
# ps -ef | grep varnishd | grep -v grep
管理進程每隔幾秒探測一下子進程的活動.如果在一定的時間之內,沒有獲得回覆,管理進程就會殺死子進程並重啓它.管理進程的日誌將會記錄到syslog.由於管理進程啓動子進程的速度很快,因此很難察覺到.通過監控syslog和varnish的啓動時間有助於解決問題.
# tail -f /var/log/messages # varnishstat -f uptime
Varnish子進程及線程模型
子進程包含幾種類型的線程,包括但不限於:
線程名稱 線程數目 任務 acceptor線程 1個 接收新連接並委派他們 cache-worker線程 每個會話(活動連接)一個 處理請求 cache-main線程 1個 用於Varnish啓動 expiry線程 1個 將舊內容從緩存中移除 ban lurker線程 1個 清除bans epoll/kqueue線程 可配置,默認爲2個 管理線程池 backend線程 每個後端探測有一個 探測健康檢查 當線程需要獲得或修改內存的時候,Varnish使用工作空間(workspaces)來減少線程之間的競爭.Varnish有多個工作空間,但用來操作會話數據的會話工作空間(session workspace)是最重要的.比如,session workspace爲5M,有1000個線程,那麼虛擬內存使用是5GB,但實際的內存使用不一定是5GB.內存控制器和操作系統會跟蹤記錄實際使用的內存.
爲了跟系統的其它部分通訊,子進程使用了文件系統可訪問的共享內存日誌(shared memory log).這意味着如果一個線程需要記錄日誌,它所需要做的就是獲得一個”鎖”,寫進內存區域,然後釋放”鎖”.此外,每個worker線程都有一個日誌數據的緩存,以減少”鎖”的競爭.
共享內存日誌文件通常大約爲90MB,並且分成兩個部分.第一部分是計數器(counters),第二部分是請求數據(request data).可以使用Varnish的輔助工具(如:varnishlog,varnishstat,varnishtop)來查看實際數據.
Varnish的每個會話都使用一個線程.Varnish可以使用的線程數決定了Varnish可以同時併發處理多少個請求.
Varnish運行時使用多個線程池.當一個連接被接受的時候,該連接會被委派給其中一個線程池.這個線程池會進一步委派這個被接受的連接給可用的worker線程.如果沒有可用的worker線程,則將該連接放到queue.如果Queue已經滿了,則該連接被丟棄.Varnish默認使用2個線程池,這已經被證明足夠用,即使對很忙的Varnish服務器也適用.VCL編譯
Varnsih緩存策略的配置是通過VLC(Varnish配置語言)來實現的.管理進程會解析VCL並轉化爲C,然後由C編譯器來編譯.最終,編譯後的VCL會被關聯到正在運行的Varnish實例.編譯後的VCL文件一直被保留直到重啓Varnish或通過管理客戶端的vcl.discard命令丟棄它.
在Varnish不停止運行的情況下,可通過管理客戶端重新加載VCL配置,新VLC的緩存策略會立即生效.
查看當前Varnish實例的可用vcl列表:
# varnishadm vcl.list available 0 boot available 0 reload_2013-04-06T23:00:44 available 0 reload_2013-04-06T23:00:47 available 0 reload_2013-04-06T23:00:48 active 0 reload_2013-04-06T23:00:49
編譯一個新的vcl,並加載使用:
# varnishadm vcl.load full_vcl /usr/local/varnish/etc/varnish/full_fdfs.vcl # varnishadm vcl.list available 0 boot available 0 reload_2013-04-06T23:00:44 available 0 reload_2013-04-06T23:00:47 available 0 reload_2013-04-06T23:00:48 active 0 reload_2013-04-06T23:00:49 available 0 full_vcl # varnishadm vcl.use full_vcl
丟棄一個名爲”reload_2013-04-06T23:00:44″的vcl配置:
# varnishadm vcl.discard reload_2013-04-06T23:00:44
Varnish存儲方式
Varnish支持幾種方式來給緩存分配空間,包括:file,malloc和persistent (experimental).
malloc存儲方式:
Varnish會使用malloc(內存分配)請求分配整個預設緩存大小的內存.操作系統通過使用swap將不能裝載到內存的數據存儲到磁盤.file存儲方式:
Varnish會在文件系統上創建一個文件去包含整個緩存,並且告訴操作系統通過mmap(內存映射)將整個文件映射到內存中(如果可能的話),即file方式實際上也是使用內存去緩存數據.
需要注意的是,在停止或重啓Varnish的時候,file存儲的方式不會保留數據.使用file存儲方式,Varnish不會記錄哪些數據寫入到磁盤,哪些沒有寫入.因此不可能知道磁盤上的緩存是否可用,它僅僅是隨機數據.如果使用file存儲方式的話,Varnish不會(也不可以)重新使用舊的緩存.persistent:
類似file存儲方式,是一種試驗性的持久存儲方式.它還沒有很好解決空間全部消耗完的情況.存儲方式選擇
選擇存儲方式的時候,如果內存足夠大的話,最好選擇malloc,這樣可以讓緩存全部或是大部分保存在內存中.
如果需要緩存的數據超過可用物理內存,可以選擇file存儲方式.Varnish額外開銷
通過-s參數設置的是預設的實際緩存大小.Varnish還需要額外的開銷來跟蹤記錄緩存,大概每個儲存對象需要約1K的額外開銷.(100萬的對象就需要額外使用1GB內存).此外,啓動Varnish(無任何存儲對象時)也需要大概100MB左右的開銷.
Varnish性能調優
Varnish參數
查看Varnish當前的所有參數設置:
# varnishadm 'param.show -l'
優化參數的設置可以通過管理客戶端varnishadm設置,也可以在通過varnishd的啓動參數-p來設置.
通過varnishadm設置的參數不會被保存,Varnish重啓的時候將會丟失.最好的設置方式將參數放到啓動腳本.系統內核調優
# echo 'fs.file-max=1000000' >> /etc/sysctl.conf # echo 'net.ipv4.ip_local_port_range = 1024 65535' >> /etc/sysctl.conf # sysctl -p
# echo '* soft nofile 1000000' >> /etc/security/limits.conf # echo '* hard nofile 1000000' >> /etc/security/limits.conf # echo 'session required pam_limits.so' >> /etc/pam.d/login # ulimit -n 1000000 # ulimit -n -H
注:不要設置tw_reuse爲1 (sysctl).那樣會對NAT的客戶端造成問題.
Varnish啓動參數調優
Varnish的默認參數大多已經是優化的了.需要調優的可能是線程數(number of threads)和工作空間(workspace).
thread_pools:worker線程池的數量,默認值爲2.
增加worker線程池數量可以減少”鎖”的競爭.但是過多的線程池會消耗CPU和RAM的資源,線程池數目多於CPU的核數目對性能是有損害的.
儘管可使用多個線程池,但生產環境和經驗已經證明有2個線程池之後,增加更多的線程池並不會提高性能.thread_pool_min:每個線程池的最小的線程數,默認值爲5.
thread_pool_max:每個線程池的最大的線程數,默認值是500.
不要將該值設置得太高,因爲過量的worker線程會消耗RAM和CPU,從而適得其反.
worker線程數受文件描述符數限制,總的worker線程數不應該超過5000,否則可能出現文件描述符相關的問題.thread_pool_add_delay:創建線程之間最少需要等待的時間,默認值爲2ms.
設置時間過長會導致worker線程不足,設置過短會增加worker線程堆積的風險.
使用2ms就好,可以快速創建線程,避免發生隊列填滿和丟棄請求,減少Varnish的啓動時間.thread_pool_timeout:線程空閒的閾值(在被移除之前),默認值300s.該值很少需要改變.
當線程數目超過thread_pool_min,並且處於空閒狀態至少這麼久的時間,就很可能被清除.thread_pool_fail_delay:在線程創建失敗之後,創建另一個線程需要至少等待多少時間.
創建worker線程失敗通常是系統出問題的徵兆.因爲進程已經消耗完留給線程堆棧(thread stacks)的RAM資源.
這樣的延遲可以減少不必要的擁擠狀況.
如果線程創建失敗成爲了問題,那麼請檢查thread_pool_max是否設置太高.增加thread_pool_timeout和thread_pool_min也可能有助於降低線程銷燬和重新創建的速率.lru_interval:儲存對象在LRU列表上移動之前的寬限時間.
存儲對象只能被移動到LRU列表的前面,如果他們在這個時間之內還沒有被移動到那裏的話.
這樣可以減少給LRU列表訪問的“鎖”操作的數量.不要修改該值,否則可能會很危險(如果你突然需要LRU列表的話).sess_workspace: 分配給會話的進入的HTTP頭的工作空間(來自客戶端).默認是64K,最小需要1K.
這個空間必須足夠大,分配給整個HTTP協議頭部和任何在VCL中對HTTP頭的修改.調優的話,可以取值16k~10M.session_linger:爲了看是否有新的請求馬上出現,worker線程在一個會話上逗留多長時間.默認值爲50ms.
如果會話被重複使用,在之前一次的請求完成之後的頭100ms內,最多隻有一半的重用發生.
設置該值過高會導致worker線程不會爲這樣的逗留時間做任何事情,設置過低意味着更多的會話繞過了等待.
在CPU吃緊的時候,讓每個worker線程等待幾個新的請求很重要,這樣可以避免過多的上下文切換.
該值的設置取決於分發存儲對象的通常需要多少時間.此外,這也會減少線程堆積的數量.sess_timeout:留給持久會話的空閒超時(即keep-alive timeout).默認是5s.(來自客戶端)
如果一個HTTP請求在這麼多時間之內還沒有被接受,那麼這個會話就會被關閉.
調高sess_timeout的值,會增加文件描述符的消耗.這樣做沒有什麼好處,還可能會造成文件描述符耗盡的危險.send_timeout:客戶端連接的發送超時.默認值爲60s.
如果HTTP響應數據在這麼多的時間內沒有被傳輸,那麼會話就被關閉.cli_timeout:子進程迴應來自管理進程CLI請求的超時時間.默認是10s.
cli_timeout是管理進程在認定子線程死掉之前,等待子線程進行迴應的超時時間.如果超過這個時間,管理進程就會殺掉子進程,並將它重啓.Varnish負載比較大的話,管理進程可能會出現無法及時管理子線程而造成誤殺.適當調高該值可以避免這種情況發生.
優化varnishd的啓動參數設置例子:
-p thread_pools = 2 -p thread_pool_min = 200 -p thread_pool_max = 2000 -p thread_pool_add_delay=2 -p session_linger = 100 -p sess_workspace=262144 -p cli_timeout=25
通常使用500~1000個總的最小線程來運行Varnish.通過監控n_wrk_queued可以知道這樣的線程設置是否合適.
n_wrk_queued(queued work requests)在Varnish啓動之後應該是幾乎靜止不變的.# varnishstat -f n_wrk_queued
VCL參數優化
connect_timeout:後端的默認連接超時(OS/network延遲).默認是0.7s
Varnish在放棄連接之前只會嘗試連接後端這麼多時間.VCL中可以針對每個後端和後端請求覆蓋該值.first_byte_timeout:接收後端的第一個字節數據的默認超時(頁面生成?).默認是60s.
Varnish在放棄之前只等待這麼長時間去接收第一個字節數據.如果設置該值爲0,意味着永遠不超時.
VCL中可以針對每個後端和後端請求覆蓋該值.該參數不適用於pipe.between_bytes_timeout:接收後端數據時字節之間的默認超時.默認是60s.
Varnish在放棄之前在字節之間只等待這麼多時間.如果該值爲0,意味着永遠不超時.
CL中可以針對每個後端和後端請求覆蓋該值.該參數不適用於pipe.
VCL後端邏輯例子:
... backend default { .host = "192.168.1.191"; .port = "80"; .connect_timeout = 1s; .first_byte_timeout = 100s; .between_bytes_timeout = 100s; .probe = { .url = "/status.php"; .interval = 20s; .timeout = 10s; .window = 2; .threshold = 2; } } ...
轉載:http://www.zrwm.com/?cat=138 作者:Jose