前面的話
Web服務器每天會分發出數以億計的Web頁面,它是萬維網的骨干。本文主要介紹WEB服務器的相關內容
總括
Web服務器會對HTTP請求進行處理並提供響應。術語“Web服務器”可以用來表示Web服務器的軟件,也可以用來表示提供Web頁面的特定設備或計算機。Web服務器有着不同的風格、形狀和尺寸,但不管功能有何差異,所有的Web服務器都能夠接收請求資源的HTTP請求,將內容回送給客戶端
Web服務器邏輯實現了HTTP協議、管理着Web資源,並負責提供Web服務器的管理功能。Web服務器邏輯和操作系統共同負責管理TCP連接。底層操作系統負責管理底層計算機系統的硬件細節,並提供了TCP/IP網絡支持、負責裝載Web資源的文件系統以及控制當前計算活動的進程管理功能
Web服務器有各種不同的形式:可以在標准的計算機系統上安裝並運行通用的軟件Web服務器;可以是一台Web服務器設備,通常會是一台安裝在機架上的計算機,里面的軟件會預裝並配置好;在少量計算機芯片上也可以實現嵌入式Web服務器
【通用軟件Web服務器】
通用軟件Web服務器運行在標准的、有網絡功能的計算機系統上。可以選擇開源軟件(比如Apache)或者商業軟件(比如微軟和iPlanet的Web服務器)。基本上所有的計算機和操作系統中都有可用的Web服務器軟件
【Web服務器設備】
Web服務器設備(Web server appliance)是預先打包好的軟硬件解決方案。廠商會在他們選擇的計算機平台上預先安裝好軟件服務器,並將軟件配置好。應用解決方案不再需要安裝及配置軟件,通常可以極大地簡化管理工作。但是,Web服務器通常不太靈活,特性不太豐富,而且服務器硬件也不太容易重用或升級
【嵌入式Web服務器】
嵌入式服務器(embeded server)是要嵌入到消費類產品(比如打印機或家用設備)中去的小型Web服務器。嵌入式Web服務器允許用戶通過便捷的Web瀏覽器接口來管理其消費者設備。有些嵌入式Web服務器甚至可以在小於一平方英寸的空間內實現,但通常只能提供最小特性功能集
WEB服務器一般要執行以下任務:
1、建立連接——接受一個客戶端連接,或者如果不希望與這個客戶端建立連接,就將其關閉
2、接收請求——從網絡中讀取一條HTTP請求報文
3、處理請求——對請求報文進行解釋,並采取行動
4、訪問資源——訪問報文中指定的資源
5、構建響應——創建帶有正確首部的HTTP響應報文
6、發送響應——將響應回送給客戶端
7、記錄事務處理過程——將與已完成事務有關的內容記錄在一個日志文件中
接下來的幾個小節重點說明了Web服務器是怎樣實現這些基本任務的
接受連接
如果客戶端已經打開了一條到服務器的持久連接,可以使用那條連接來發送它的請求。否則,客戶端需要打開一條新的到服務器的連接
【處理新連接】
客戶端請求一條到Web服務器的TCP連接時,Web服務器會建立連接,判斷連接的另一端是哪個客戶端,從TCP連接中將IP地址解析出來。一旦新連接建立起來並被接受,服務器就會將新連接添加到其現存Web服務器連接列表中,做好監視連接上數據傳輸的准備
Web服務器可以隨意拒絕或立即關閉任意一條連接。有些Web服務器會因為客戶端IP地址或主機名是未認證的,或者因為它是已知的惡意客戶端而關閉連接。Web服務器也可以使用其他識別技術
【客戶端主機名識別】
可以用“反向DNS”對大部分Web服務器進行配置,以便將客戶端IP地址轉換成客戶端主機名。Web服務器可以將客戶端主機名用於詳細的訪問控制和日志記錄。但要注意的是,主機名査找可能會花費很長時間,這樣會降低Web事務處理的速度。很多大容量Web服務器要么會禁止主機名解析,要么只允許對特定內容進行解析
接收報文
連接上有數據到達時,Web服務器會從網絡連接中讀取數據,並將請求報文中的內容解析出來
解析請求報文時,Web服務器會:解析請求行,查找請求方法、指定的資源標識符(URI)以及版本號,各項之間由一個空格分隔,並以一個回車換行(CRLF)序列作為行的結束;讀取以CRLF結尾的報文首部;檢測到以CRLF結尾的、標識首部結束的空行(如果有的話);如果有的話(長度由Content-Length首部指定),讀取請求主體
解析請求報文時,Web服務器會不定期地從網絡上接收輸入數據。網絡連接可能隨時都會出現延遲。Web服務器需要從網絡中讀取數據,將部分報文數據臨時存儲在內存中,直到收到足以進行解析的數據並理解其意義為止
有些Web服務器還會用便於進行報文操作的內部數據結構來存儲請求報文。比如,數據結構中可能包含有指向請求報文中各個片段的指針及其長度,這樣就可以將這些首部存放在一個快速査詢表中,以便快速訪問特定首部的具體值

【連接的輸入/輸出處理結構】
高性能的Web服務器能夠同時支持數千條連接。這些連接使得服務器可以與世界各地的客戶端進行通信,每個客戶端都向服務器打開了一條或多條連接。某些連接可能在快速地向Web服務器發送請求,而其他一些連接則可能在慢慢發送,或者不經常發送請求,還有一些可能是空閑的,安靜地等待着將來可能出現的動作
因為請求可能會在任意時刻到達,所以Web服務器會不停地觀察有無新的Web請求。不同的Web服務器結構會以不同的方式為請求服務
a、單線程Web服務器
單線程的Web服務器一次只處理一個請求,直到其完成為止。一個事務處理結束之后,才去處理下一條連接。這種結構易於實現,但在處理過程中,所有其他連接都會被忽略。這樣會造成嚴重的性能問題,只適用於低負荷的服務器
b、多進程及多線程Web服務器
多進程和多線程Web服務器用多個進程,或更高效的線程同時對請求進行處理。可以根據需要創建,或者預先創建一些線程/進程。有些服務器會為每條連接分配一個線程/進程,但當服務器同時要處理成百、上千,甚至數以萬計的連接時,需要的進程或線程數量可能會消耗太多的內存或系統資源。因此,很多多線程Web服務器都會對線程/進程的最大數量進行限制
c、復用I/O的服務器
為了支持大量的連接,很多Web服務器都采用了復用結構。在復用結構中,要同時監視所有連接上的活動。當連接的狀態發生變化時(比如,有數據可用,或出現錯誤時),就對那條連接進行少量的處理;處理結束之后,將連接返回到開放連接列表中,等待下一次狀態變化。只有在有事情可做時才會對連接進行處理,在空閑連接上等待的時候並不會綁定線程和進程
d、復用的多線程Web服務器
有些系統會將多線程和復用功能結合在一起,以利用計算機平台上的多個CPU。多個線程(通常是一個物理處理器)中的每一個都在觀察打開的連接(或打開的連接中的一個子集),並對每條連接執行少量的任務

處理請求
一旦Web服務器收到了請求,就可以根據方法、資源、首部和可選的主體部分來對請求進行處理
有些方法(比如POST)要求請求報文中必須帶有實體主體部分的數據。其他一些方法(比如OPTIONS)允許有請求的主體部分,也允許沒有。少數方法(比如GET)禁止在請求報文中包含實體的主體數據
訪問資源
Web服務器是資源服務器。它們負責發送預先創建好的內容,比如HTML頁面或JPEG圖片,以及運行在服務器上的資源生成程序所產生的動態內容。在Web服務器將內容傳送給客戶端之前,要將請求報文中的URI映射為Web服務器上適當的內容或內容生成器,以識別出內容的源頭
【docroot】
Web服務器支持各種不同類型的資源映射,但最簡單的資源映射形式就是用請求URI作為名字來訪問Web服務器文件系統中的文件。通常,Web服務器的文件系統中會有一個特殊的文件夾專門用於存放Web內容。這個文件夾被稱為文檔的根目錄(document root,或docroot)。Web服務器從請求報文中獲取URI,並將其附加在文檔根目錄的后面
下圖中,有一條對/specials/saw-blade.gif的請求到達。這個例子中Web服務器的文檔根目錄為/usr/local/httpd/files。Web服務器會返回文件/usr/local/httpd/files/specials/saw-blade.gif

在配置文件httpd.conf中添加一個DocumentRoot行就可以為Apache Web服務器設置文檔的根目錄了:
DocumentRoot /usr/local/httpd/files
虛擬托管的Web服務器會在同一台Web服務器上提供多個Web站點,每個站點在服務器上都有自己獨有的文檔根目錄。虛擬托管Web服務器會根據URI或Host首部的IP地址或主機名來識別要使用的正確文檔根目錄。通過這種方式,即使請求URI完全相同,托管在同一Web服務器上的兩個Web站點也可以擁有完全不同的內容
下圖中的服務器托管了兩個站點:www.joes-hardware.com和www.marys-antiques.com。服務器可以通過HTTP的Host首部,或根據不同的IP地址來區分不同的Web站點。當請求A到達時,服務器會獲取文件/docs/joe/index.html;當請求B到達時,服務器會獲取文件/docs/mary/index.html

對大多數Web服務器來說,配置虛擬托管的文檔根目錄是很簡單的。對常見的Apache Web服務器來說,需要為每個虛擬Web站點配置一個VirtualHost塊,而且每個虛擬服務器都要包含DocumentRoot
Docroot的另一種常見應用是在Web服務器上為人們提供私有的Web站點。通常會把那些以斜杠和波浪號(/~)開始,后面跟着用戶名的URI映射為此用戶的私有文檔根目錄。私有docroot通常都是用戶主目錄下那個名為public_html的目錄,但也可將其配置為其他值

【目錄列表】
Web服務器可以接收對目錄URL的請求,其路徑可以解析為一個目錄,而不是文件。可以對大多數Web服務器進行配置,使其在客戶端請求目錄URL時采取不同的動作:返回一個錯誤;或不返回目錄,返回一個特殊的默認“索引文件”;或掃描目錄,返回一個包含目錄內容的HTML頁面
大多數Web服務器都會去査找目錄中一個名為index.html或index.htm的文件來代表此目錄。如果用戶請求的是一個目錄的URL,而且這個目錄中有一個名為index.html(或indeX.htm)的文件,服務器就會返回那個文件的內容
在Apache Web服務器上,可以用配置指令DirectoryIndex來配置要作為默認目錄文件使用的文件名集合。指令DirectoryIndex會按照優先順序列出所有可以作為目錄索引文件使用的文件名。下列配置行會使Apache在收到一個目錄URL請求時,在目錄中搜索命令中列出來的任意一個文件:
DirectoryIndex index.html index.htm home.html home.htm index.cgi
如果用戶請求目錄URI時,沒有提供默認的索引文件,而且沒有禁止使用目錄索引,很多Web服務器都會自動返回一個HTML文件,此文件中會列出那個目錄里
的文件名,以及每個文件的大小和修改日期,還包括到每個文件的URI鏈接。使用這個文件列表可能會很方便,也可以通過它在Web服務器上找到一些通常找不到的東西
可以通過以下Apache指令禁止自動生成目錄索引文件:
Options - Indexes
【動態映射】
Web服務器可以將URI映射為動態資源——也就是說,映射到按需動態生成內容的程序上去。實際上,有一大類名為應用程序服務器的Web服務器會將Web服務器連接到復雜的后端應用程序上去。Web服務器要能夠分辨出資源什么時候是動態的,動態內容生成程序位於何處,以及如何運行那個程序。大多數Web服務器都提供了一些基本的機制以識別和映射動態資源
Apache允許用戶將URI路徑名組件映射為可執行文件目錄。服務器收到一條帶有可執行路徑組件的對URI的請求時,會試着去執行相應服務器目錄中的程序。例如,下列Apache配置指令就表明所有路徑以/cgi-bin/開頭的URI都應該執行在目錄/usr/local/etc/httpd/cgi-programs/中找到的相應文件:
ScriptAlias/cgi-bin/usr/local/etc/httpd/cgi-programs/
Apache還允許用戶用一個特殊的文件擴展名來標識可執行文件。通過這種方式就可以將可執行腳本放在任意目錄中了。下面的Apache配置指令說明要執行所有以.cgi結尾的Web資源
AddHandler cgi-script .cgi
CGI是早期出現的一種簡單、流行的服務端應用程序執行接口。現代的應用程序服務器都有更強大更有效的服務端動態內容支持機制,包括微軟的動態服務器頁面(Active Server Page)和Java servlet
很多Web服務器還提供了對服務器端包含項(SSI)的支持。如果某個資源被標識為存在服務器端包含項,服務器就會在將其發送給客戶端之前對資源內容進行處理
要對內容進行掃描,以査找(通常包含在特定HTML注釋中的)特定的模板,這些模板可以是變量名,也可以是嵌入式腳本。可以用變量的值或可執行腳本的輸出來取代特定的模板。這是創建動態內容的一種簡便方式
Web服務器還可以為特定資源進行訪問控制。有請求到達,要訪問受控資源時,Web服務器可以根據客戶端的IP地址進行訪問控制,也可以要求輸入密碼來訪問資源
構建響應
一旦Web服務器識別出了資源,就執行請求方法中描述的動作,並返回響應報文。響應報文中包含有響應狀態碼、響應首部,如果生成了響應主體的話,還包括響應主體
如果事務處理產生了響應主體,就將內容放在響應報文中回送過去。如果有響應主體的話,響應報文中通常包括:描述了響應主體MIME類型的Content-Type首部;描述了響應主體長度的Content-Length首部;實際報文的主體內容
【MIME 類型】
Web服務器要負責確定響應主體的MIME類型。有很多配置服務器的方法可以將MIME類型與資源關聯起來
Web服務器可以用文件的擴展名來說明MIME類型。Web服務器會為每個資源掃描一個包含了所有擴展名的MIME類型的文件,以確定其MIME類型。這種基於擴展名的類型相關是最常見的

a、魔法分類(Magic typing)
Apache Web服務器可以掃描每個資源的內容,並將其與一個已知模式表(被稱為魔法文件)進行匹配,以決定每個文件的MIME類型。這樣做可能比較慢,但很方便,尤其是文件沒有標准擴展名的時候
b、顯式分類(Explicit typing)
對Web服務器進行配置,使其不考慮文件的擴展名或內容,強制特定文件或目錄內容擁有某個MIME類型
c、類型協商
有些Web服務器經過配置,可以以多種文檔格式來存儲資源。在這種情況下,可以配置Web服務器,使其可以通過與用戶的協商來決定使用哪種格式(及相關的MIME類型)“最好”
還可以通過配置Web服務器,將特定的文件與MIME類型相關聯
【重定向】
Web服務器有時會返回重定向響應而不是成功的報文。Web服務器將瀏覽器重定向到其他地方來執行請求。重定向響應由返回碼3XX說明。Location響應首部包含了內容的新地址或優選地址的URI。重定向用於下列情況
a、永久刪除的資源
資源可能已經被移動到了新的位置,或者被重新命名,有了一個新的URL。Web服務器可以告訴客戶端資源已經被重命名了,這樣客戶端就可以在從新地址獲取資源之前,更新書簽之類的信息了。狀態碼301 Moved Permanently就用於此類重定向
b、臨時搬離的資源
如果資源被臨時移走或重命名了,服務器可能希望將客戶端重定向到新的位置上去。但由於重命名是臨時的,所以服務器希望客戶端將來還可以回頭去使用老的URL,不要對書簽進行更新。狀態碼303 See Other以及狀態碼307 Temporary Redirect就用於此類重定向
c、URL增強
服務器通常用重定向來重寫URL,往往用於嵌入上下文。當請求到達時,服務器會生成一個新的包含了嵌入式狀態信息的URL,並將用戶重定向到這個新的URL上去。客戶端會跟隨這個重定向信息,重新發起請求,但這次的請求會包含完整的、經過狀態增強的URL。這是在事務間維護狀態的一種有效方式。狀態碼 303 See Other 和 307 Temporary Redirect用於此類重定向
[注意]有時會將這些經過擴展和狀態增強的URL稱為"胖URL"
d、負載均衡
如果一個超載的服務器收到一條請求,服務器可以將客戶端重定向到一個負載不太重的服務器上去。狀態碼303 See Other和307 Temporary Redirect可用於此類重定向
e、服務器關聯
Web服務器上可能會有某些用戶的本地信息,服務器可以將客戶端重定向到包含了那個客戶端信息的服務器上去。狀態碼303 See Other和307 Temporary Redirect可用於此類重定向
f、規范目錄名稱
客戶端請求的URI是一個不帶尾部斜線的目錄名時,大多數Web服務器都會將客戶端重定向到一個加了斜線的URI上,這樣相對鏈接就可以正常工作了
發送響應
Web服務器通過連接發送數據時也會面臨與接收數據一樣的問題。服務器可能有很多條到各個客戶端的連接,有些是空閑的,有些在向服務器發送數據,還有一些在向客戶端回送響應數據
服務器要記錄連接的狀態,還要特別注意對持久連接的處理。對非持久連接而言,服務器應該在發送了整條報文之后,關閉自己這一端的連接。對持久連接來說,連接可能仍保持打開狀態,在這種情況下,服務器要特別小心,要正確地計算Content-Length首部,不然客戶端就無法知道響應什么時候結束
記錄日志
最后,當事務結束時,Web服務器會在日志文件中添加一個條目,來描述已執行的事務