Apache HTTP Server 版本2.2
虛擬主機部分的代碼在Apache 1.3中進行了完全的重寫。本文檔試圖詳細解釋Apache在接受到請求后如何確定使用哪一個虛擬主機進行伺服。在新的NameVirtualHost
指令的幫助下,虛擬主機的配置比1.3版以前更加簡單和安全。
如果您只是想讓它能夠工作而不愿意進行深入理解,這里有一些示例。
在<VirtualHost>
配置段外有一個主服務(wù)器(main_server)段中包含著所有定義。其中有<VirtualHost>
配置段中定義的叫做虛擬主機(vhost)的虛擬服務(wù)器。
Listen
, ServerName
, ServerPath
, ServerAlias
指令可以出現(xiàn)在一個服務(wù)器定義段的任何地方。而且每個指令都會覆蓋前面出現(xiàn)的同樣定義(在那個服務(wù)器配置中)。
主服務(wù)器段中Listen
指令的默認值是80。主服務(wù)器段沒有默認的ServerPath
和ServerAlias
指令值。ServerName
的默認值是由服務(wù)器的IP地址推斷而來。
主服務(wù)器的Listen
指令有兩個功能:其一是決定Apache將要綁定的網(wǎng)絡(luò)端口;其二是在重定向中指定絕對URI將使用的端口號。
不象在主服務(wù)器里,虛擬服務(wù)器的端口不會影響到Apache的監(jiān)聽端口。
每個VirtualHost
指令中的地址都可以附帶一個可選的端口。如果沒有進行特別的指定,這個端口默認為主服務(wù)器中最近的一個Listen
指令指定的值。特殊的端口"*
"表示匹配所有端口。所有這一系列地址(包括由DNS查詢出的所有A
記錄)統(tǒng)稱虛擬主機的地址集(address set)。
如果沒有對一個特定的IP地址使用NameVirtualHost
指令,那么第一個使用這個地址的虛擬主機將被視為基于IP的虛擬主機。IP地址也可以用通配符"*
"表示。
如果使用了基于域名的虛擬主機,那么必須用NameVirtualHost
指令為這個基于域名的虛擬主機指定IP地址集。換句話說,您必須在配置文件中通過NameVirtualHost
指令指定包括主機名映射(CNAME)的IP地址。
可以使用很多NameVirtualHost
指令來分別對應(yīng)一套NameVirtualHost
指令,但對于每個特定的"IP:port"對來說,只能使用一次NameVirtualHost
指令。
NameVirtualHost
和VirtualHost
指令出現(xiàn)的順序并不重要。只有對應(yīng)同一個IP地址的VirtualHost
指令的次序才是重要的。所以下面兩例所起的作用是完全相同的:
|
|
(為了使您的配置文件更具可讀性,我們推薦您使用左邊的格式)
在解析完VirtualHost
指令后,虛擬主機服務(wù)器將被賦予在它的VirtualHost
指令中第一個名字對應(yīng)的端口作為默認的Listen
端口。
如果所有域名都指向同一個地址集的話,VirtualHost
指令中的所有域名列表都將會得到和ServerAlias
指令一樣的處理(但不會被其他ServerAlias
語句覆蓋)。請注意,這個虛擬主機自帶的Listen
指令將不能影響到那個地址集的端口號。
在初始化的過程中,將會為每一個IP地址產(chǎn)生一個列表,并插入到一個散列表中。如果這個IP地址是用在一個NameVirtualHost
指令中的,這個列表將會包含所有指定為這個IP地址的基于域名的虛擬主機。如果沒有虛擬主機針對這個IP地址,那么NameVirtualHost
指令將被忽略,并會在日志中記錄一個錯誤信息。對于基于IP的虛擬主機而言,這個散列表中的列表為空。
因為使用了高效的散列算法,使得在請求到達的時候在其中查找IP地址的開銷變得很小,或者根本不需考慮。而且這個表格還為只有最后一個八進制位不同的IP地址做了優(yōu)化。
虛擬主機的每個變量都有初始值。特別是以下這些:
ServerAdmin
, ResourceConfig
, AccessConfig
, Timeout
, KeepAliveTimeout
, KeepAlive
, MaxKeepAliveRequests
, ReceiveBufferSize
, SendBufferSize
指令,那么將從主服務(wù)器繼承它們的值。(也就是說,使用在主服務(wù)器中最后出現(xiàn)的設(shè)定值)。本質(zhì)上,主服務(wù)器在建立每個虛擬主機的時候,充當(dāng)了一個默認值或根基的角色。但這些存在于主服務(wù)器中的定義的位置是無關(guān)緊要的——主服務(wù)器的配置在與虛擬主機整合之前就已經(jīng)解析過了。所以即使一個主服務(wù)器的配置出現(xiàn)在虛擬主機定義的后面,它也同樣會影響到虛擬主機的配置。
如果沒有定義主服務(wù)器中的ServerName
,那么將由運行這個httpd
服務(wù)的機器的主機名來代替。我們將由DNS查找此ServerName
返回的IP地址稱為主服務(wù)器地址集(main_server address set)。
在沒有定義ServerName
的情況下,一個基于域名的虛擬主機默認采用定義虛擬主機時在VirtualHost
指令中最先出現(xiàn)的地址。
所有使用了"_default_
"通配符的虛擬主機將被賦予和主服務(wù)器相同的ServerName
。
服務(wù)器用下述方法來確定對一個特定的請求使用哪個虛擬主機:
當(dāng)客戶端第一次連接的時候,會從內(nèi)部的IP散列表中查找客戶端想要連接的IP地址。
如果查找失敗(沒有找到相應(yīng)的IP地址),而所請求的端口又存在一個"_default_
"虛擬主機,那么這個請求將會由這個虛擬主機來伺服。如果沒有找到這樣的"_default_
"虛擬主機,那么這個請求將會由主服務(wù)器來伺服。
如果在散列表中沒有找到IP地址,但存在一個"NameVirtualHost *
"指令與所請求的端口號相匹配,那么將用這個虛擬主機來處理這個請求。
如果查找成功(找到了對應(yīng)于這個IP地址的列表),下一步就是看我們要處理的是一個基于IP的虛擬主機還是一個基于域名的虛擬主機。
如果返回的列表中域名列表為空,那么我們處理的就是一個基于IP的虛擬主機,這個虛擬主機將會直接進行處理而不會有其他步驟。
如果返回的域名列表包含一個或多個虛擬主機的結(jié)構(gòu),那么我們處理的就是一個基于域名的虛擬主機。這個列表包含的虛擬主機的順序與配置文件中相應(yīng)VirtualHost
指令出現(xiàn)的順序是相同的。
這個列表中第一個虛擬主機(也就是在配置文件中第一個指定了這個IP地址的虛擬主機)對處理請求有著最高的優(yōu)先級。所有對未知服務(wù)器名或沒有"Host:
"頭的請求都將由它進行處理。
如果客戶端在請求中提供了一個"Host:
"頭,那么將在列表中查找第一個ServerName
或ServerAlias
與其符合的虛擬主機,并將其用于伺服這個請求。盡管"Host:
"頭中可以包含端口號,但Apache還是會用收到請求的那個真實端口來進行匹配。
如果客戶端提交了一個不包含"Host:
"頭的HTTP/1.0的請求,我們將無法確認客戶端想要連接那個服務(wù)器。而如果存在一個ServerPath
與客戶端提交的請求中的URI相對應(yīng),那么列表中第一個符合條件的虛擬主機將用于伺服這個請求。
如果還是找不到對應(yīng)的虛擬主機,那么這個請求將會由客戶端連接的IP對應(yīng)的列表中的第一個與請求的端口相同的虛擬主機來伺服(如前所述)。
上述IP查找對一個特定的TCP/IP進程只執(zhí)行一次。但在持久連接(KeepAlive)中,每個請求都會進行一次這樣的查找過程。換句話說,一個客戶端在一個持久連接中可以向位于不同的基于域名的虛擬主機的頁面提出請求。
如果請求提交的URI是一個絕對URI,而其中的主機名和端口號又和主服務(wù)器或某個虛擬主機相符合,并且也與作為此請求提交對象的地址和端口相符,那么這個請求的類型/主機名/端口前綴將被抹除,僅留下相對URI為對應(yīng)的主服務(wù)器或虛擬主機所伺服。如果不滿足上述符合條件,這個URI將保留原樣,而此請求將被作為一個代理請求處理。
NameVirtualHost
指令定義的地址集的訪問。ServerAlias
和ServerPath
檢查。_default_
"虛擬主機和NameVirtualHost
指令出現(xiàn)的順序并不重要。而對于某個指定的地址集來說,基于域名的虛擬主機的順序是不能混淆的:在配置文件中較先出現(xiàn)的虛擬主機在相應(yīng)的地址集中有較高的優(yōu)先權(quán)。Host:
"頭中出現(xiàn)的端口號將不用于匹配。Apache會一直使用客戶端所連接的真實端口作為匹配。ServerPath
指令湊巧是后面出現(xiàn)的另外一個ServerPath
指令的前綴,前者將用于匹配,而后者將被忽略。(這里討論的是沒有"Host:
"頭來將這兩個情況分開的情況下)_default_
"虛擬主機才會捕獲這個請求。并且僅當(dāng)"_default_
"虛擬主機的端口號(默認值由您的Listen
指定)與客戶端發(fā)送請求的目的端口號相符時,這個請求才會被捕獲。也可以使用通配符(例如:"_default_:*
")來捕獲任何端口號的請求。這也同樣適用于"NameVirtualHost *
"的虛擬主機。_default_
"虛擬主機)匹配的時候,才會用主服務(wù)器來伺服請求。換句話說,主服務(wù)器僅捕獲沒有指定IP地址和端口的請求(除非存在一個匹配端口的"_default_
"虛擬主機)。NameVirtualHost
指令,那么一個未知的或沒有"Host:
"頭的請求就不會與"_default_
"虛擬主機或是主服務(wù)器相匹配。VirtualHost
指令中使用DNS名稱,否則您的服務(wù)器就會依賴DNS來進行啟動。而且,如果您無法控制列表中所有的域,您將會面臨安全威脅。您可以在這里獲得關(guān)于這個問題和以下兩個問題的更多詳情。ServerName
。否則就會需要為每個虛擬主機進行DNS查詢。作為DNS問題頁面小技巧的附加,這里有些額外的技巧:
VirtualHost
定義之前(為了增加可讀性),否則會使得類似在虛擬主機旁邊的定義影響到所有的虛擬主機這樣的問題不容易發(fā)現(xiàn)。NameVirtualHost
和VirtualHost
定義放到一起,以獲得更好的可讀性。ServerPaths
是后一個ServerPaths
的前綴。如果您無法避免這樣的情況,您最好確保在您的配置文件中"長在前,短在后"(也就是說:"ServerPath/abc/def"應(yīng)當(dāng)出現(xiàn)在"ServerPath/abc"之前)。