使用 Apache Web 服務器作為示例,以了解如何分析公共配置的性能含義。使用應用程序跟蹤來觀察應用程序運行過程中進行的系統調用。通過統計調用的次數和發生的時間,您可以輕松地了解性能改變的影響。
您可以對應用程序進行跟蹤,以找出它們暫?;虿贿\行的原因。并且可以使用同樣的方法,了解更多關于應用程序的信息并理解某些配置的性能含義。因為 Apache 非常流行并且大多數讀者對它都比較熟悉,所以本文使用 Apache 作為示例。Apache 所進行的每次系統調用都會對 Web 頁面的提供帶來延遲,通過跟蹤不同配置下的 Web 服務器,您可以確定具體配置的影響。
應用程序跟蹤概述
在應用程序的執行過程中,當需要打開文件、發送數據包或者使用系統資源時,它會對基礎操作系統進行相應的系統調用。跟蹤應用程序意味著可以在調用發生時觀察到這些系統調用,這將使得您能夠深入地了解該應用程序的行為。在 Solaris 和 IBM AIX® 操作系統 (AIX) 中,使用 truss
命令完成這項任務,而在 Linux® 中則使用 strace
。清單 1 顯示了對 pwd
命令進行跟蹤的示例。
|
在刪除開始處與加載該應用程序相關的輸出內容后,您可以看到所進行的三次系統調用:
getcwd
返回當前工作目錄。輸出內容中顯示了字符串“/export/home/sean”返回到緩沖區。 write
可以顯示給定的字符串。因為在其執行之后才顯示這個系統調用,所以先輸出了它的執行結果。還可以注意到,write
系統調用的結果是寫入字符的個數數目,在這個示例中為 17 加上一個回車。 _exit
使用錯誤代碼 0 退出該程序,這個錯誤代碼通常表示成功結束。
盡管這是一個很簡單的示例,但它演示了通過應用程序跟蹤可以觀察程序內部機制的程度。有關跟蹤方面更深入的信息,請參見參考資料部分。
![]() ![]() |
![]()
|
Apache 配置的簡要介紹
可以通過一個名為 httpd.conf 的文件對 Apache Web 服務器進行配置。清單 2 顯示了一個簡單配置中的部分內容。
|
第一行定義了在何處可以找到 HTML 文件。將所有的請求都映射到這個目錄。如果請求 /project/charter.html,將使用 /var/apache/htdocs/project/charter.html 提供該頁面。httpd.conf 中剩余的部分由兩個 Directory
節組成。<Directory ...>
和 </Directory>
之間的任何內容都將應用于指定目錄及其所有的子目錄。在本例中,第一節將兩項設置應用于根目錄,而第二節則指向 /var/apache/htdocs。
如果多個節應用于單個請求,那么將會對這些命令進行組合,并且最適合的目錄具有高優先級。例如,將使用 /var/apache/htdocs/project/charter.html 提供 /project/charter.html 請求的頁面。/var/apache/htdocs 是 / 的子目錄,所以 Options Indexes FollowSymLinks MultiViews
來自第二節,AllowOverride None
來自第一節。
可以對許多內容進行配置,并且每項內容都具有其性能含義。本文余下的內容重點關注于如何對這些改變的影響進行量化分析。
![]() ![]() |
![]()
|
建立基準
在您進行任何調整之前,務必要了解系統當前的運行方式。使用 -X
參數啟動 Apache,這個參數將強制 Apache 進入單進程調試模式。這樣做可以確保將請求發送到正在進行跟蹤的進程,并且消除常規進程間通信所帶來的開銷。
在守護進程啟動之后,通過運行 ps -ef
找到相應的進程 ID,并查找 httpd 守護進程。在找到該進程之后,使用 truss -c -p PID
附加到該進程。-c
選項表示對系統調用進行計數,而不是逐個顯示它們,而 -p
則表示將跟蹤器附加于一個正在運行的進程。
使用 Web 瀏覽器請求文檔。在頁面加載之后,回到 truss
應用程序,然后按 Ctrl-C 以結束計數。對于靜態 HTML 頁面,您應該看到如清單 3 所示的內容(為使這些數字變得更有趣,本示例進行了 100 次相同的請求)。
|
truss
返回系統調用的列表、執行調用耗費的總時間、調用的次數和任何發現的問題。在這個報表的結尾處,返回了這些系統調用耗費的總時間,以及執行應用程序所耗費的時間。對于這些目的來說,所耗時間是沒有意義的,因為它指的是從啟動 truss
開始到其結束的時間,而與 Web 請求沒有任何關系。
清單 3 顯示了最簡單的情況。在來自 Web 瀏覽器的連接請求到達后,accept
系統調用完成該連接。Web 服務器使用 read
調用獲得請求的內容,將請求的內容映射到磁盤上的文件。Web 服務器首先使用 stat64
驗證該文件是否存在,使用 open64
打開該文件以便進行讀取,然后使用 mmap64
將該文件的內容映射到內存中。然后使用 write
將這個文件發送回客戶端,使用另一個 write
生成日志文件,并且服務器執行來自瀏覽器的最后一個 read
。該列表中其余的調用都是系統開銷,并且當配置發生改變時,不會有顯著的變化。
解釋這些數值
100 次請求總共耗費 0.269 秒 (0.149 + 0.120),這樣的性能相當不錯,并且該服務器每秒鐘應該可以提供大約 370 個頁面 (100/0.269)。但是不能完全相信這些數值,因為它們僅表示進程耗費在 CPU 上的時間,而不是其真正的執行時間(也稱為時鐘時間)。還有更多的因素需要考慮,如磁盤和網絡的速度、計算機上正運行的其他內容、該守護進程運行于調試模式的事實。您還需要考慮系統調用跟蹤本身的開銷。
本文中使用的方法重點關注這些操作的相對計時和使用應用程序跟蹤消除浪費掉的操作。如果您需要了解 Web 服務器每秒可以提供的頁面數目,參考資料部分中有相應的軟件鏈接,它可以幫助您確定該數值。
![]() ![]() |
![]()
|
跟蹤 AllowOverride 范圍
Apache 允許管理員通過 .htaccess 機制將配置權委托給個別的用戶。.htaccess 是一個包含附加配置指令的文件,如果在 httpd.conf 中通過 AllowOverride
配置了請求的目錄,那么 Web 服務器將搜索這個文件。清單 4 顯示了前面的配置了 AllowOverride Limit
的配置信息,它允許用戶獲取訪問 Web 頁面的用戶名和密碼。
|
重新啟動 httpd
守護進程并再次運行這些測試,其結果如清單 5 所示。
|
初看起來,系統調用耗費的時間下降了,但這是因為使用了 -t
選項將跟蹤任務限制于一些有意義的系統調用。大多數系統調用并沒有發生變化,但現在有 500 次 open64
調用,其中有 400 次返回了錯誤。執行 open64
的時間增加了(從 0.006 秒增加到 0.22 秒),同時用戶空間部分的時間也增加了(從 0.12 秒增加到 0.141 秒)。
時間增加是因為 Apache 現在必須完成附加的工作以處理該請求,即使沒有配置重寫。單獨的 AllowOverride Limit
配置明顯地增加了開銷。問題依然存在,即什么導致了這些錯誤? 要回答這個問題,可以跟蹤單個 Web 請求,如清單 6 所示。
|
清單 6 顯示了當請求進入時,Apache 對每個指向 /var/apache/htdocs 的目錄進行檢查并嘗試打開 .htaccess 文件,但是因為 AllowOverride
配置為根目錄,所以這個文件并不存在。Apache 必須在每個子目錄中查找 .htaccess 文件的重寫信息,并對它們進行處理。這樣一來,由于額外的系統調用、更多的用戶空間開銷和額外的磁盤活動,從而進一步增加了延遲。對于 100 次請求來說,增加零點幾秒的時間看起來并不是很明顯,但是對于一臺繁忙的服務器,就會增加更長的延遲。
既然您了解了重寫的范圍,那么理想的解決方案是不允許重寫,并且強制在 httpd.conf 中對所有的內容進行配置。如果失敗,可以將配置的范圍限制于需要它的目錄。在這個研究示例中,把 AllowOverride Limit
放到第二個 Directory
節中,這將僅添加一個額外的 open64
調用,以便在 /var/apache/htdocs 中查找 .htaccess。對所有的父目錄進行搜索是浪費時間,因為在這個配置中,不會使用其中的任何文件來提供頁面。
![]() ![]() |
![]()
|
研究主機名查找
當 Web 服務器接收到一個請求時,它所知道的關于客戶端的信息只有其 IP 地址,如 129.42.42.212 對應于 IBM.com。然而,Web 服務器并不知道這個地址來自于 IBM.com,因此,它必須進行反向 DNS 查找。這樣做需要耗費一定的時間,如果在發送請求之前需要這個名稱,那么將會延遲對客戶端的響應。過去,Apache 在缺省情況下會執行這些反向查找,但現在這種行為已經有了改變。
還有另一種情況,其中必須進行反向 DNS 搜索。當基于主機名(而不是 IP 地址)配置訪問限制時,Apache 必須首先將 IP 地址反向解析為相應的主機名,然后再次將主機名解析為 IP 地址,以確保它們正確匹配。因為反向域名搜索可以由地址塊所有者確定,所以要防止 IP 欺騙的發生,必須進行第二次查找??梢酝ㄟ^應用程序跟蹤來確定 DNS 解析的影響嗎?
要對其進行測試,可以從前面的示例中刪除 AllowOverride Limit
,然后添加 Allow from ibm.com
代替缺省的 Allow from all
。然后,對 DNS 服務器進行更改以便向您的工作站返回 something.ibm.com
,確保初始反向檢查能夠成功并且隨后的正向查找必須通過 Internet 完成。在示例運行過程中,對保護的 Web 服務器的請求耗費了 15 秒的時間。相反,使用 IP 地址代替 ibm.com 所耗費的時間小于半秒鐘。清單 7 顯示了在使用 DNS 確保安全時,對一些系統調用進行統計。
|
truss
報告了該進程所耗費的時間遠遠小于客戶端感覺到的時間(0.015 秒與 15 秒)。這是因為大多數套接字操作都是異步地 進行的,其中套接字進行輪詢以檢查數據是否出現,而不是使得應用程序處于阻塞(等待)狀態以等待響應。如此一來,應用程序在等待結果的時候不會消耗 CPU 時間。這就解釋了 truss
報告的時間和客戶端感覺到的時間之間出現差異的原因。
truss
并沒有忘記所做的更改,0.015 秒比本文中研究的第一個簡單示例要高一個數量級。從系統調用計數中可以看出,有一些以前沒有出現過的調用,包括 read
、write
、close
和 stat
。以及還有 send
、connect
、so_socket
和 pollsys
,這些系統調用用于進行名稱請求。因為名稱解析可以來自于不同的來源,包括本地文件系統和名稱緩存守護進程,所以必須對這幾個位置進行檢查。在隨后的調用中,請求時間小于 1 秒,這是因為對 DNS 信息進行了緩存。
最后,大部分的延遲來自于遠程名稱服務器和正向解析。這個事實進一步強調了,如果您希望依賴于名稱查找,那么就需要使用 DNS 緩存和快速 DNS 服務器。然而,最佳的解決方案是使用一種可選的方法來處理這個問題。一種比較簡單的解決方案是指定 IP 地址塊(如 Allow from 10.0.0.0/8
),這種方法比 DNS 查找要快得多。因為反向和正向查找必須匹配,所以對名稱進行的操作,很可能可以用于網段。另外,Apache 可以集成各種身份驗證系統,所以基于用戶的身份驗證是另一種可選方法。
系統調用與庫調用
如果熟悉套接字編程,那么您可能會尋找 gethostbyname
和其他類似的調用,這些調用都可以用來執行主機查找功能。有一些庫調用,由 /usr/lib 中的系統庫 libc
和 libsocket
提供。庫調用封裝了一個或多個系統調用,以及額外的邏輯。您可以將它們作為程序員友好的接口來進行系統調用。例如,gethostbyname
庫調用將執行許多步驟,以便根據服務器的配置查找相應的名稱,如檢查 /etc/hosts 或搜索網絡信息系統 (NIS) 或 DNS。這些步驟包含了內核所提供的更小的構件,即系統調用。在大多數這樣的這種情況下,兩者之間的差別通常很小。truss
還可以跟蹤庫調用,但是無法提供像系統調用那樣詳細的信息。
![]() ![]() |
![]()
|
結束語
監視和統計系統調用的功能不僅有助于進行故障排除,還有助于理解應用程序配置如何對性能產生影響。與系統的其他部分之間的每個交互操作都會調用一個或多個系統調用,并且每次調用都會增加系統開銷。這并不是說系統調用非常糟糕,如果程序員不與其他的部分進行交互,那將是很乏味的。相反,在改變應用程序時,您可以監視系統調用的使用,以便更好地了解這些改變對總體性能的影響。