?
Dieses Dokument verwendet PHP-Handbuch für chinesische Websites Freigeben
這篇文檔是對mod_cache
、mod_disk_cache
、mod_mem_cache
、mod_file_cache
和htcacheclean參考文檔內(nèi)容的增補。它描述了如何利用Apache的緩沖特性來加速web和代理(proxy)服務,同時避免一些常見的問題和配置錯誤。
從Apache2.2起,mod_cache
和mod_file_cache
將不再是試驗模塊,它們已經(jīng)足夠穩(wěn)定,可以用于實際生產(chǎn)中了。這些緩沖體系提供了一個強有力的途徑來加速原始web服務器(origin webserver)和代理服務器(proxy)的HTTP處理速度。
mod_cache
以及它的支持模塊mod_mem_cache
和mod_disk_cache
提供了智能的HTTP緩沖。內(nèi)容(content)本身被存儲在緩沖區(qū)中,mod_cache
的目的在于管理控制內(nèi)容緩沖能力的各種HTTP頭和選項。它可以同時處理本地的內(nèi)容和代理的內(nèi)容。mod_cache
被設計為同時針對簡單的和復雜的緩沖配置,以用于處理代理的內(nèi)容、動態(tài)的本地內(nèi)容、必須加速訪問的隨時間而改變本地文件。
另一方面,mod_file_cache
實現(xiàn)了一個更基本的、但是在某些情況下更有效的緩沖形式,它避免了主動確保URL緩沖能力所需的維護復雜性,mod_file_cache
通過提供文件句柄(file-handle)和內(nèi)存映射(memory-mapping)的技巧來維持一個自Apache最后一次啟動以來的文件緩沖區(qū)。同樣地,mod_file_cache
的目標是改善不常修改的本地靜態(tài)文件的訪問時間。
由于mod_file_cache
提供了一個相對簡單的緩沖實現(xiàn),除了CacheFile
和MMapStatic
文檔的特定段落之外,這篇指南的說明覆蓋了mod_cache
的緩存體系結(jié)構。
為了更好的理解這篇文檔,你應當熟悉HTTP的基礎知識,并且已經(jīng)閱讀過從URL到文件系統(tǒng)的映射和內(nèi)容協(xié)商這兩篇用戶指南。
相關模塊 | 相關指令 |
---|---|
|
|
在一個請求的生存期中,mod_cache
內(nèi)可能會發(fā)生兩個主要階段。首先,mod_cache
將是一個URL映射模塊,也就是說,如果一個URL已經(jīng)被緩存并且這個緩存尚未失效,該請求將由mod_cache
直接處理。
這也意味著在處理一個請求時通常還要發(fā)生的其他階段:比如由mod_proxy
或mod_rewrite
處理的階段,將不會發(fā)生。不過,這正是將內(nèi)容緩存起來的關鍵所在。[雖然某些階段被省略了,但這正是啟用緩沖特性的初衷:減少處理步驟以提高速度。]
如果這個URL不在緩存中,mod_cache
將會在請求的處理過程中添加一個過濾器。在Apache使用通常的方法定位內(nèi)容之后,該過濾器將會在內(nèi)容被用于服務以后運行。如果該內(nèi)容被確定為可以緩存,那么它將被保存在緩沖區(qū)中以便為將來的請求提供服務。
如果該URL存在于緩存中并且已經(jīng)失效的話,該過濾器同樣會被添加,但是mod_cache
將會同時向后端(backend)提交一個條件請求以確定緩存的版本是否是當前的最新版本。如果是最新版本,那么它的元信息(meta-information)將會被更新并且使用這個緩存的版本來服務于該請求。如果不是最新版本,那么過濾器將使用剛才為請求提供服務的最新內(nèi)容更新緩存。
在緩存本地生成的內(nèi)容的時候,將UseCanonicalName
指令設置為 On
可以顯著提高緩存的命中率。這是由于為緩沖區(qū)提供內(nèi)容的虛擬主機的主機名是緩沖鍵(cache key)的組成部分。當該指令設置為 On
時,具有多個服務器名或別名的虛擬主機將不會產(chǎn)生不同的緩存實體,而是按照各自的規(guī)范主機名(canonical hostname)來存儲。
由于緩存發(fā)生在將URL映射到文件系統(tǒng)的階段,緩存的文檔將只被用來響應對URL的請求。通常情況下這沒什么重大意義,但是當你使用服務端包含(Server Side Includes)時,這一點將顯得特別重要:
<!-- 下面的包含可以被緩存 --> <!--#include virtual="/footer.html" --> <!-- 下面的包含不可以被緩存 --> <!--#include file="/path/to/footer.html" -->
如果你使用服務端包含(SSI),并且希望從緩沖中獲得快速服務好處,你應當使用virtual
類型的包含。
緩存實體的默認失效周期是一個小時(3600秒),當然這個可以輕易的通過CacheDefaultExpire
指令來修改。這個默認值僅僅用在產(chǎn)生內(nèi)容的原始資源沒有明確指定失效時間或最后修改時間的情況下。
如果一個應答沒有包含Expires
頭但卻包含Last-Modified
頭時,mod_cache
可以根據(jù)CacheLastModifiedFactor
指令推斷出失效周期。
對于本地內(nèi)容,mod_expires
可以用來調(diào)整失效周期。
失效周期的最大值還可以通過CacheMaxExpire
指令來控制。
當緩存的內(nèi)容失效并且被從后端(backend)或內(nèi)容提供者(content provider)那里重新請求的時候,Apache并不直接傳遞原始的請求,而是使用一個條件請求(conditional request)。
HTTP協(xié)議使用的一些頭(header)允許客戶端或緩沖區(qū)鑒別同一個內(nèi)容的不同版本。例如,如果一個資源應答了"Etag:"頭,那么就可以生成一個包含"If-Match:"頭的條件請求;如果一個資源應答了"Last-Modified:"頭,那么就可以生成一個包含"If-Modified-Since:"頭的條件請求;等等。
對于這樣的條件請求,應答的不同取決于內(nèi)容是否匹配這些條件。如果一個請求包含一個"If-Modified-Since:"頭,而請求的內(nèi)容在指定的時間之后并未發(fā)生改變,那么一個簡潔的"304 Not Modified"應答就可以了。
如果請求的內(nèi)容已經(jīng)變化,那么將按照原來沒有條件請求的普通方式來應答。
和緩存相關的條件請求的好處有兩個方面。首先,當向后端提交這樣的條件請求時,如果從后端獲得的內(nèi)容與存儲的內(nèi)容相匹配(這很容易確定),就可以避免由于傳遞全部資源所帶來的開銷。
其次,條件請求通常只讓后端花費較小的開銷。對于靜態(tài)文件,通常所有的開銷就是一個stat()
或類似的系統(tǒng)調(diào)用以確定改文件的大小是否變化以及最后修改時間。這樣,如果被請求的內(nèi)容尚未變化,甚至在Apache緩存的本地內(nèi)容已經(jīng)失效的情況下,仍然可以從緩沖區(qū)中快速取得以服務于請求——只要從緩沖區(qū)讀取比從后端讀取更快(例如從內(nèi)存緩沖區(qū)讀取就比從硬盤上讀取更快)。
如前所述,Apache中的緩沖存在兩種不同工作方式。mod_file_cache
的緩沖區(qū)負責維護Apache啟動時的文件內(nèi)容。當一個存在于該模塊緩沖區(qū)中的文件被請求時,該請求將被攔截并用緩沖區(qū)中的內(nèi)容為其提供服務。
mod_cache
的緩沖區(qū)相對而言較為復雜。當服務于一個請求時,如果它先前并未被緩存,則緩沖模塊將會判斷該內(nèi)容是否可以被緩存。判斷應答的緩沖能力(cachability)基于以下條件:
CacheEnable
和CacheDisable
指令。CacheIgnoreNoLastMod
指令,否則該應答還必須至少包含一個"Etag"、"Last-Modified"或"Expires"頭才能被緩存。CacheStorePrivate
指令,否則將不被緩存。CacheStoreNoStore
指令,否則將不被緩存。簡而言之,隨時間變化的內(nèi)容不應該被緩存;取決于特定請求的內(nèi)容不應該被緩存;依賴于不被HTTP內(nèi)容協(xié)商處理的請求的內(nèi)容也不應該被緩存。[本句翻譯的很沒把握,原文:In short, any content which is highly time-sensitive, or which varies depending on the particulars of the request that are not covered by HTTP negotiation, should not be cached.]
如果你有某些動態(tài)內(nèi)容,它們的變化依賴于請求發(fā)起者的IP地址或者差不多每5分鐘就會發(fā)生改變,那么這些內(nèi)容毫無疑問是不應該被緩存的。
另一方面,如果內(nèi)容的變化依賴于各種HTTP頭,更加明智的做法可能是通過使用"Vary"頭進行緩存。
當mod_cache
接收到一個后端發(fā)出的、帶有"Vary"頭的應答的時候,它將盡可能智能的處理它。如果有機會,mod_cache
將會檢查之后進入的請求的"Vary"頭屬性,然后用正確的緩沖區(qū)內(nèi)容為這個請求提供服務。
舉個例子來說,接收到一個帶有如下"Vary"頭的應答:
Vary: negotiate,accept-language,accept-charset
mod_cache
將只會使用與原始請求的accept-language和accept-charset頭匹配的緩存內(nèi)容來提供服務。
mod_cache
非常像一個內(nèi)置的反向代理(reverse-proxy)。除非必須要向后端提交請求,否則緩沖模塊將直接為請求提供服務。對于緩沖本地資源,這種模式徹底改變了Apache的安全模型。
因為遍歷文件系統(tǒng)的目錄結(jié)構以尋找可能存在的.htaccess
文件是一個開銷非常昂貴的操作,它部分地抵消了緩沖所帶來的好處(加速請求),所以mod_cache
并不檢查緩存中的實體是否被允許(authorised)用于提供服務。換句話說,只要mod_cache
中緩存的內(nèi)容尚未失效,那么它們將被直接用于為請求提供服務。
舉例來說,如果你為某個資源按照IP地址配置了訪問許可,你必須要確保這些內(nèi)容不被緩存。你可以使用CacheDisable
指令或mod_expires
模塊達到這個目的。不做權限檢查的mod_cache
模塊非常像一個反向代理:緩存內(nèi)容并用緩存的內(nèi)容向任意IP地址上的任意客戶提供服務。
因為終端用戶的請求可以由緩沖區(qū)直接提供服務,所以緩沖區(qū)自身便成為一個那些企圖干擾、破壞內(nèi)容的攻擊者的攻擊目標。很重要的、需要牢記的一點是:緩沖區(qū)必須始終對運行Apache的用戶是可寫的。這正好與通常的原則:始終保持所有內(nèi)容對運行Apache的用戶不可寫,完全相反!
如果運行Apache的用戶是潛在的不安全用戶,比如,通過一個有漏洞的CGI進程,就有可能對緩沖區(qū)發(fā)起攻擊,當使用mod_disk_cache
的時候,就很容易插入或者修改緩沖區(qū)中內(nèi)容。
這樣一來,運行Apache的用戶就會增加一個與其它類型的攻擊相比更加危險的安全隱患。如果你正在使用mod_disk_cache
,你必須時刻牢記:確保為Apache及時打上所有的安全補丁并且使用suEXEC以一個不同于運行Apache用戶的其他用戶身份運行CGI進程。
當將Apache作為一個緩沖代理服務器運行的時候,將可能存在一個所謂"緩存中毒"的問題。"緩存中毒"是一個泛稱術語,用于指代各種造成代理服務器從后臺檢索到錯誤內(nèi)容的攻擊。
舉個例子來說,如果你運行Apache的系統(tǒng)所使用的DNS服務器發(fā)生了DNS緩存中毒,攻擊者將可能欺騙Apache連接到一個錯誤的服務器去請求內(nèi)容。另一個例子是所謂的HTTP請求走私(request-smuggling)攻擊。
這篇文檔并不是深入探討HTTP請求走私的地方(你應當去問google),但有一點你必須知道:攻擊者可以通過制造一連串的請求并利用原始web服務器的漏洞,達到完全控制代理服務器所檢索到的內(nèi)容的目的。
相關模塊 | 相關指令 |
---|---|
|
|
打開文件的動作本身就是一個造成延時的原因,特別是打開網(wǎng)絡文件系統(tǒng)中的文件。通過維護一個保存高使用率文件的文件描述符的緩沖區(qū),Apache就可以避免這種延時。當前,Apache提供了兩種不同的文件句柄緩沖實現(xiàn)方法。
存在于Apache中最基本的緩沖方式是由mod_file_cache
實現(xiàn)的文件句柄(file-handle)緩沖。勝于緩存文件內(nèi)容本身,這個緩沖區(qū)維護一張打開的文件描述符表,用于保存在配置文件中使用CacheFile
指令指定的文件的文件句柄。
CacheFile
指令指示Apache在啟動時打開某個文件并且為所有之后對這個文件的訪問重復使用這個文件句柄。
CacheFile /usr/local/apache2/htdocs/index.html
如果你打算使用這種方式緩存大量的文件句柄,你必須確保操作系統(tǒng)對同時打開的文件的數(shù)量限制是足夠的。
雖然使用CacheFile
不會導致文件的內(nèi)容被緩存,但是將會導致在Apache運行期間所有對文件的更改都不會生效。用于提供服務的文件的內(nèi)容將從Apache啟動以來一直保持不變。
如果在Apache運行期間文件被刪除了,Apache將會持續(xù)維護一個打開的文件描述符并且使用Apache啟動時文件的內(nèi)容來提供服務。這個通常也意味著雖然文件已經(jīng)被刪除,并且不在文件系統(tǒng)中顯示出來,但是釋放的空間并不會被覆蓋,直到Apache被停止、文件描述符被關閉。
mod_mem_cache
也提供了一個文件句柄緩沖方案,可以通過CacheEnable
指令來啟用。
CacheEnable fd /
與mod_cache
的方案相比,這種方案更加智能:緩存內(nèi)容失效以后相應的句柄將不再被維護。
相關模塊 | 相關指令 |
---|---|
|
|
直接從系統(tǒng)的內(nèi)存中提供服務通常是取得服務內(nèi)容最快速的方法。從一個磁盤控制器讀取文件,或者更糟糕的是從遠程網(wǎng)絡讀取文件,其速度要慢上幾個數(shù)量級。磁盤控制器通常涉及到物理動作,訪問網(wǎng)絡要受限于網(wǎng)絡帶寬,而訪問內(nèi)存通常僅僅只需要幾毫微秒時間。
內(nèi)存也許是目前單位字節(jié)最昂貴的存儲器,保證它充分發(fā)揮作用非常重要。將文件緩存在內(nèi)存中將導致系統(tǒng)可用內(nèi)存的減少。正如我們將要看到的,在操作系統(tǒng)存在內(nèi)存緩沖區(qū)的情況下,這不是一個大問題。但是當使用Apache自己的內(nèi)存緩沖區(qū)的情況下,確保沒有為緩沖區(qū)分配太多的內(nèi)存就顯得十分重要。否則,操作系統(tǒng)將會使用swap(虛擬內(nèi)存/交換區(qū)),這可能會導致性能急劇下降。
幾乎所有現(xiàn)代的操作系統(tǒng)都由內(nèi)核直接管理文件數(shù)據(jù)在內(nèi)存中的緩沖。這是一個強有力的特性,并且在極大程度上操作系統(tǒng)做的非常好。比如在Linux系統(tǒng)上,讓我們看看第一次讀取一個文件和第二次讀取同樣的文件所需要的時間:
colm@coroebus:~$ time cat testfile > /dev/null real 0m0.065s user 0m0.000s sys 0m0.001s colm@coroebus:~$ time cat testfile > /dev/null real 0m0.003s user 0m0.003s sys 0m0.000s
即使對于這樣的一個小文件,兩次讀取的時間差異也十分驚人。這是由于內(nèi)核在內(nèi)存中緩存了文件的內(nèi)容。
通過確保在你的系統(tǒng)上始終存在"多余的"內(nèi)存,你就可以確保會有越來越多的文件內(nèi)容被緩存在這個緩沖區(qū)中。這是一個非常有效的內(nèi)存緩沖途徑,并且根本無需對Apache作出任何額外的配置。
另外,由于操作系統(tǒng)知道文件何時被修改或刪除了,它就可以自動的從內(nèi)存緩沖區(qū)中刪除失效的文件內(nèi)容。這是一個優(yōu)于Apache自身的內(nèi)存緩沖區(qū)的巨大優(yōu)點,因為Apache無法得知文件被修改或刪除的信息。
盡管操作系統(tǒng)自動管理的緩沖區(qū)有著性能和洞悉文件狀態(tài)的優(yōu)勢,但是在某些情況下Apache自己的內(nèi)存緩沖卻更加有效。
首先,操作系統(tǒng)只能緩存它自己知道的文件,如果你將Apache當作一個代理服務器運行,那么Apache可以緩存非本地文件。如果你還想要無可匹敵的內(nèi)存緩存速度,也必須使用Apache自己的內(nèi)存緩沖區(qū)。
mod_file_cache
提供了MMapStatic
指令,它可以指示Apache在啟動時將一個靜態(tài)文件的內(nèi)容映射到內(nèi)存中(使用mmap()系統(tǒng)調(diào)用)。Apache將會使用內(nèi)存中緩存的內(nèi)容來為后來對這個文件的訪問提供內(nèi)容。
MMapStatic /usr/local/apache2/htdocs/index.html
使用了CacheFile
指令以后,在Apache運行期間,對這些文件所做的任何修改都不會生效。
MMapStatic
指令并不關心它占用了多少內(nèi)存,所以你必須確保不要過度濫用這個指令。每個Apache子進程都將復制這部分內(nèi)存,所以非常重要的一點是你必須確保被映射的文件不能占用太多的內(nèi)存,以至于操作系統(tǒng)不得不使用交換區(qū)或虛擬內(nèi)存。
mod_mem_cache
提供了一個智能的HTTP內(nèi)存緩沖方案。它同時也直接使用堆內(nèi)存,這也意味著即使MMap不被你的操作系統(tǒng)所支持,mod_mem_cache
仍然能夠?qū)崿F(xiàn)緩沖。
這種類型的緩沖可以通過以下方法啟用:
# 啟用內(nèi)存緩沖 CacheEnable mem / # 將緩沖區(qū)的大小限制為 1 MB MCacheSize 1024
相關模塊 | 相關指令 |
---|---|
|
|
mod_disk_cache
為mod_cache
提供了一個基于磁盤的緩沖機制。和mod_mem_cache
一樣,這是一種智能緩沖,僅在緩存內(nèi)容沒有失效的情況下才從緩沖區(qū)中提供服務。
通常,這個模塊將按如下方式進行配置:
CacheRoot /var/cache/apache/ CacheEnable disk / CacheDirLevels 2 CacheDirLength 1
請注意,因為緩沖區(qū)位于本地磁盤上,所以操作系統(tǒng)的內(nèi)存緩沖區(qū)通常對它們的訪問也有效。所以雖然這些文件被存儲在本地磁盤上,但若這些文件被頻繁的訪問,那么很可能操作系統(tǒng)已經(jīng)將它們保存在內(nèi)存中了。
要將項目保存在緩沖區(qū)中,mod_disk_cache
會為被請求的URL創(chuàng)建一個22字符的哈希值。該哈希值包含了該URL的主機名、協(xié)議、端口、路徑、CGI變量,以確保多個URL不會發(fā)生碰撞。
這22個字符的取值范圍是64個不同的字符,這意味著最多可以有22^64種可能的取值。例如,一個URL的哈希值可能是:xyTGxSMO2b68mBCykqkp1w
。這個哈希值將被用作緩存中對應于那個URL的文件名前綴,但是首先,這個哈希值將被按照CacheDirLevels
和CacheDirLength
指令分解成每一級目錄名。
CacheDirLevels
指定了子目錄的層數(shù),CacheDirLength
指定了每級子目錄名的字符數(shù)。使用上述例子的設置,這個哈希值將被轉(zhuǎn)化成如下文件名前綴:/var/cache/apache/x/y/TGxSMO2b68mBCykqkp1w
。
使用這種技術的總體目標是減少某個特定目錄中子目錄或文件的個數(shù),因為絕大多的文件系統(tǒng)在子目錄或文件數(shù)過多的情況下的訪問速度都會大打折扣。將CacheDirLength
設置為"1"將使得任意一層目錄下的子目錄數(shù)都不會超過64,若為設為"2"則為64*64,依此類推。除非你有一個非常好的理由,否則"1"將是CacheDirLength
指令的推薦值。
如何設置CacheDirLevels
指令的值取決于你預計到將會在緩沖區(qū)中保存多少個文件。上述示例使用的"2"將會導致大約會有4096個子目錄最終被建立,大約100萬個文件被緩存,大約平均每個文件夾存儲245個URL緩沖文件。
每個URL在緩沖區(qū)中至少會使用兩個文件。通常,一個是包含了URL元信息(meta-information)的".header"文件,比如何時失效;另一個是".data"文件,包含了按字節(jié)復制的用于為URL提供服務的內(nèi)容。
在通過使用"Vary"頭進行內(nèi)容協(xié)商的情況下,將會為該URL創(chuàng)建一個".vary"目錄,該目錄下將會保存多個適合不同協(xié)商內(nèi)容的".data"文件。
雖然mod_disk_cache
將會刪除緩沖區(qū)中失效的文件,但是它并不負責維護整個緩沖區(qū)總共究竟應該占據(jù)多大空間以及至少要保留多少剩余空間。
作為彌補,Apache附帶了一個htcacheclean工具,正如你從它的名字猜到的,它可以周期性的清理緩沖區(qū)。確定htcacheclean的運行頻率以及緩沖區(qū)應當占有多大的磁盤空間是一件復雜的事情。必須要經(jīng)過多次嘗試和碰壁才能找到一個最佳值。
htcacheclean有兩種運作模型。一種是作為后臺守護進程運行,或者由cron周期性的調(diào)用。htcacheclean經(jīng)常使用一個小時或更多的時間來處理非常巨大的(幾十G)緩沖區(qū),所以如果你是使用cron來調(diào)用它的話,建議你測試一下多長時間運行一次比較合適,以避免在同一時間運行多個實例。
圖1: 一個典型的緩沖區(qū)增長和清理的周期
因為mod_disk_cache
模塊自身并不關心究竟實際使用了多少磁盤空間,所以你必須確保htcacheclean被配置為在清理了緩沖區(qū)以后預留了足夠多的"增長空間"。