主頁 > 知識(shí)庫(kù) > APACHE性能方面的提示

APACHE性能方面的提示

熱門標(biāo)簽:怎樣使用奧維地圖標(biāo)注位置 濟(jì)南呼叫中心外呼系統(tǒng)如何 黃石外呼saas系統(tǒng) 揭陽外呼系統(tǒng)收費(fèi) 鳳城市地圖標(biāo)注app 菏澤監(jiān)獄親情電話機(jī)器人 模型地圖標(biāo)注 臨沂ai電銷機(jī)器人價(jià)格 地圖標(biāo)注怎么上交呢

Apache2.0是一個(gè)多用途的web服務(wù)器,其設(shè)計(jì)在靈活性、可移植性和性能中求得平衡。雖然沒有在設(shè)計(jì)上刻意追求性能指標(biāo),但是Apache2.0仍然在許多現(xiàn)實(shí)環(huán)境中擁有很高的性能。

相比于Apache 1.3 ,2.0版本作了大量的優(yōu)化來提升處理能力和可伸縮性,而且大多數(shù)的改進(jìn)在默認(rèn)狀態(tài)下就可以生效。但是,在編譯時(shí)和運(yùn)行時(shí),都有許多可以顯著提高性能的選擇。本文闡述在安裝Apache2.0時(shí),服務(wù)器管理員可以改善性能的各種方法。其中,部分配置選擇可以使httpd更好地利用硬件和操作系統(tǒng)的兼容性,其他則是以功能換取速度。
top
硬件和操作系統(tǒng)

影響web服務(wù)器性能的最大的因素是內(nèi)存。一個(gè)web服務(wù)器應(yīng)該從不使用交換機(jī)制,因?yàn)榻粨Q產(chǎn)生的滯后使用戶總感覺"不夠快",所以用戶就可能去按"停止"和"刷新",從而帶來更大的負(fù)載。你可以,也應(yīng)該,控制MaxClients的設(shè)置,以避免服務(wù)器產(chǎn)生太多的子進(jìn)程而發(fā)生交換。這個(gè)過程很簡(jiǎn)單:通過top命令計(jì)算出每個(gè)Apache進(jìn)程平均消耗的內(nèi)存,然后再為其它進(jìn)程留出足夠多的內(nèi)存。

其他因素就很普通了,裝一個(gè)足夠快的CPU,一個(gè)足夠快的網(wǎng)卡,幾個(gè)足夠快的硬盤,這里說的"足夠快"是指能滿足實(shí)際應(yīng)用的需求。

操作系統(tǒng)是很值得關(guān)注的又一個(gè)因素,已經(jīng)被證實(shí)的很有用的經(jīng)驗(yàn)有:

  *

    選擇能夠得到的最新最穩(wěn)定的版本并打好補(bǔ)丁。近年來,許多操作系統(tǒng)廠商都提供了可以顯著改善性能的TCP協(xié)議棧和線程庫(kù)。
  *

    如果你的操作系統(tǒng)支持sendfile()系統(tǒng)調(diào)用,則務(wù)必安裝帶有此功能的版本或補(bǔ)丁(對(duì)Linux來說,就是使用Linux2.4或更高版本,對(duì)Solaris8的早期版本,則需要安裝補(bǔ)丁)。在支持sendfile的系統(tǒng)中,Apache2可以更快地發(fā)送靜態(tài)內(nèi)容而且占用較少的CPU時(shí)間。

top
運(yùn)行時(shí)的配置
相關(guān)模塊   相關(guān)指令

  * mod_dir
  * mpm_common
  * mod_status

 

  * AllowOverride
  * DirectoryIndex
  * HostnameLookups
  * EnableMMAP
  * EnableSendfile
  * KeepAliveTimeout
  * MaxSpareServers
  * MinSpareServers
  * Options
  * StartServers

HostnameLookups 和其他DNS考慮

在Apache1.3以前的版本中,HostnameLookups默認(rèn)被設(shè)為 On 。它會(huì)帶來延遲,因?yàn)閷?duì)每一個(gè)請(qǐng)求都需要作一次DNS查詢。在Apache1.3中,它被默認(rèn)地設(shè)置為 Off 。如果需要日志文件提供主機(jī)名信息以生成分析報(bào)告,則可以使用日志后處理程序logresolve ,以完成DNS查詢,而客戶端無須等待。

推薦你最好是在其他機(jī)器上,而不是在web服務(wù)器上執(zhí)行后處理和其他日志統(tǒng)計(jì)操作,以免影響服務(wù)器的性能。

如果你使用了任何"Allow from domain"或"Deny from domain"指令(也就是domain使用的是主機(jī)名而不是IP地址),則代價(jià)是要進(jìn)行兩次DNS查詢(一次正向和一次反向,以確認(rèn)沒有作假)。所以,為了得到最高的性能,應(yīng)該避免使用這些指令(不用域名而用IP地址也是可以的)。

注意,可以把這些指令包含在Location /server-status>段中使之局部化。在這種情況下,只有對(duì)這個(gè)區(qū)域的請(qǐng)求才會(huì)發(fā)生DNS查詢。下例禁止除了.html和.cgi以外的所有DNS查詢:

HostnameLookups off
Files ~ "\.(html|cgi)$">
HostnameLookups on
/Files>

如果在某些CGI中偶爾需要DNS名稱,則可以調(diào)用gethostbyname來解決。
FollowSymLinks 和 SymLinksIfOwnerMatch

如果網(wǎng)站空間中沒有使用 Options FollowSymLinks ,或使用了 Options SymLinksIfOwnerMatch ,Apache就必須執(zhí)行額外的系統(tǒng)調(diào)用以驗(yàn)證符號(hào)連接。文件名的每一個(gè)組成部分都需要一個(gè)額外的調(diào)用。例如,如果設(shè)置了:

DocumentRoot /www/htdocs
Directory />
Options SymLinksIfOwnerMatch
/Directory>

在請(qǐng)求"/index.html"時(shí),Apache將對(duì)"/www"、"/www/htdocs"、"/www/htdocs/index.html"執(zhí)行l(wèi)stat()調(diào)用。而且lstat()的執(zhí)行結(jié)果不被緩存,因此對(duì)每一個(gè)請(qǐng)求都要執(zhí)行一次。如果確實(shí)需要驗(yàn)證符號(hào)連接的安全性,則可以這樣:

DocumentRoot /www/htdocs
Directory />
Options FollowSymLinks
/Directory>

Directory /www/htdocs>
Options -FollowSymLinks +SymLinksIfOwnerMatch
/Directory>

這樣,至少可以避免對(duì)DocumentRoot路徑的多余的驗(yàn)證。注意,如果Alias或RewriteRule中含有DocumentRoot以外的路徑,那么同樣需要增加這樣的段。為了得到最佳性能,應(yīng)當(dāng)放棄對(duì)符號(hào)連接的保護(hù),在所有地方都設(shè)置FollowSymLinks ,并放棄使用SymLinksIfOwnerMatch 。
AllowOverride

如果網(wǎng)站空間允許覆蓋(通常是用.htaccess文件),則Apache會(huì)試圖對(duì)文件名的每一個(gè)組成部分都打開.htaccess ,例如:

DocumentRoot /www/htdocs
Directory />
AllowOverride all
/Directory>

如果請(qǐng)求"/index.html",則Apache會(huì)試圖打開"/.htaccess"、"/www/.htaccess"、"/www/htdocs/.htaccess"。其解決方法和前面所述的 Options FollowSymLinks 類似。為了得到最佳性能,應(yīng)當(dāng)對(duì)文件系統(tǒng)中所有的地方都使用 AllowOverride None 。
內(nèi)容協(xié)商

實(shí)踐中,內(nèi)容協(xié)商的好處大于性能的損失,如果你很在意那一點(diǎn)點(diǎn)的性能損失,則可以禁止使用內(nèi)容協(xié)商。但是仍然有個(gè)方法可以提高服務(wù)器的速度,就是不要使用通配符,如:

DirectoryIndex index

而使用完整的列表,如:

DirectoryIndex index.cgi index.pl index.shtml index.html

其中最常用的應(yīng)該放在前面。

還有,建立一個(gè)明確的type-map文件在性能上優(yōu)于使用"Options MultiViews",因?yàn)樗行枰男畔⒍荚谝粋€(gè)單獨(dú)的文件中,而無須搜索目錄。請(qǐng)參考內(nèi)容協(xié)商文檔以獲得更詳細(xì)的協(xié)商方法和創(chuàng)建type-map文件的指導(dǎo)。
內(nèi)存映射

在Apache2.0需要搜索被發(fā)送文件的內(nèi)容時(shí),比如處理服務(wù)器端包含時(shí),如果操作系統(tǒng)支持某種形式的mmap() ,則會(huì)對(duì)此文件執(zhí)行內(nèi)存映射。

在某些平臺(tái)上,內(nèi)存映射可以提高性能,但是在某些情況下,內(nèi)存映射會(huì)降低性能甚至影響到httpd的穩(wěn)定性:

  *

    在某些操作系統(tǒng)中,如果增加了CPU,mmap還不如read()迅速。比如,在多處理器的Solaris服務(wù)器上,關(guān)閉了mmap ,Apache2.0傳送服務(wù)端解析文件有時(shí)候反而更快。
  *

    如果你對(duì)作為NFS裝載的文件系統(tǒng)中的一個(gè)文件進(jìn)行了內(nèi)存映射,而另一個(gè)NFS客戶端的進(jìn)程刪除或者截?cái)嗔诉@個(gè)文件,那么你的進(jìn)程在下一次訪問已經(jīng)被映射的文件內(nèi)容時(shí),會(huì)產(chǎn)生一個(gè)總線錯(cuò)誤。

如果有上述情況發(fā)生,則應(yīng)該使用 EnableMMAP off 關(guān)閉對(duì)發(fā)送文件的內(nèi)存映射。注意:此指令可以被針對(duì)目錄的設(shè)置覆蓋。
Sendfile

在Apache2.0能夠忽略將要被發(fā)送的文件的內(nèi)容的時(shí)候(比如發(fā)送靜態(tài)內(nèi)容),如果操作系統(tǒng)支持sendfile() ,則Apache將使用內(nèi)核提供的sendfile()來發(fā)送文件。

在大多數(shù)平臺(tái)上,使用sendfile可以通過免除分離的讀和寫操作來提升性能。然而在某些情況下,使用sendfile會(huì)危害到httpd的穩(wěn)定性

  *

    一些平臺(tái)可能會(huì)有Apache編譯系統(tǒng)檢測(cè)不到的有缺陷的sendfile支持,特別是將在其他平臺(tái)上使用交叉編譯得到的二進(jìn)制文件運(yùn)行于當(dāng)前對(duì)sendfile支持有缺陷的平臺(tái)時(shí)。
  *

    對(duì)于一個(gè)掛載了NFS文件系統(tǒng)的內(nèi)核,它可能無法可靠的通過自己的cache服務(wù)于網(wǎng)絡(luò)文件。

如果出現(xiàn)以上情況,你應(yīng)當(dāng)使用"EnableSendfile off"來禁用sendfile 。注意,這個(gè)指令可以被針對(duì)目錄的設(shè)置覆蓋。
進(jìn)程的建立

在Apache1.3以前,MinSpareServers, MaxSpareServers, StartServers的設(shè)置對(duì)性能都有很大的影響。尤其是為了應(yīng)對(duì)負(fù)載而建立足夠的子進(jìn)程時(shí),Apache需要有一個(gè)"漸進(jìn)"的過程。在最初建立StartServers數(shù)量的子進(jìn)程后,為了滿足MinSpareServers設(shè)置的需要,每一秒鐘只能建立一個(gè)子進(jìn)程。所以,對(duì)一個(gè)需要同時(shí)處理100個(gè)客戶端的服務(wù)器,如果StartServers使用默認(rèn)的設(shè)置5,則為了應(yīng)對(duì)負(fù)載而建立足夠多的子進(jìn)程需要95秒。在實(shí)際應(yīng)用中,如果不頻繁重新啟動(dòng)服務(wù)器,這樣還可以,但是如果僅僅為了提供10分鐘的服務(wù),這樣就很糟糕了。

" 一秒鐘一個(gè)"的規(guī)定是為了避免在創(chuàng)建子進(jìn)程過程中服務(wù)器對(duì)請(qǐng)求的響應(yīng)停頓,但是它對(duì)服務(wù)器性能的影響太大了,必須予以改變。在Apache1.3中,這個(gè) "一秒鐘一個(gè)"的規(guī)定變得寬松了,創(chuàng)建一個(gè)進(jìn)程,等待一秒鐘,繼續(xù)創(chuàng)建第二個(gè),再等待一秒鐘,繼而創(chuàng)建四個(gè),如此按指數(shù)級(jí)增加創(chuàng)建的進(jìn)程數(shù),最多達(dá)到每秒 32個(gè),直到滿足MinSpareServers設(shè)置的值為止。

從多數(shù)反映看來,似乎沒有必要調(diào)整MinSpareServers, MaxSpareServers, StartServers 。如果每秒鐘創(chuàng)建的進(jìn)程數(shù)超過4個(gè),則會(huì)在ErrorLog中產(chǎn)生一條消息,如果產(chǎn)生大量此消息,則可以考慮修改這些設(shè)置??梢允褂胢od_status的輸出作為參考。

與進(jìn)程創(chuàng)建相關(guān)的是由MaxRequestsPerChild引發(fā)的進(jìn)程的銷毀。其默認(rèn)值是"0",意味著每個(gè)進(jìn)程所處理的請(qǐng)求數(shù)是不受限制的。如果此值設(shè)置得很小,比如30,則可能需要大幅增加。在SunOS或者Solaris的早期版本上,其最大值為10000以免內(nèi)存泄漏。

如果啟用了持久鏈接,子進(jìn)程將保持忙碌狀態(tài)以等待被打開連接上的新請(qǐng)求。為了最小化其負(fù)面影響,KeepAliveTimeout的默認(rèn)值被設(shè)置為5秒,以謀求網(wǎng)絡(luò)帶寬和服務(wù)器資源之間的平衡。在任何情況下此值都不應(yīng)當(dāng)大于60秒,參見most of the benefits are lost。
top
編譯時(shí)的配置
選擇一個(gè)MPM

Apache 2.x 支持插入式并行處理模塊,稱為多路處理模塊(MPM)。在編譯Apache時(shí)你必須選擇也只能選擇一個(gè)MPM,這里有幾個(gè)針對(duì)非UNIX系統(tǒng)的MPM:beos, mpm_netware, mpmt_os2, mpm_winnt。對(duì)類UNIX系統(tǒng),有幾個(gè)不同的MPM可供選擇,他們都會(huì)影響到httpd的速度和可伸縮性:

  * workerMPM使用多個(gè)子進(jìn)程,每個(gè)子進(jìn)程中又有多個(gè)線程。每個(gè)線程處理一個(gè)請(qǐng)求。該MPM通常對(duì)高流量的服務(wù)器是一個(gè)不錯(cuò)的選擇。因?yàn)樗萷reforkMPM需要更少的內(nèi)存且更具有伸縮性。
  * preforkMPM使用多個(gè)子進(jìn)程,但每個(gè)子進(jìn)程并不包含多線程。每個(gè)進(jìn)程只處理一個(gè)鏈接。在許多系統(tǒng)上它的速度和workerMPM一樣快,但是需要更多的內(nèi)存。這種無線程的設(shè)計(jì)在某些情況下優(yōu)于workerMPM:它可以應(yīng)用于不具備線程安全的第三方模塊(比如php3/4/5),且在不支持線程調(diào)試的平臺(tái)上易于調(diào)試,而且還具有比workerMPM更高的穩(wěn)定性。

關(guān)于MPM的更多內(nèi)容,請(qǐng)參考其文檔。
模塊

既然內(nèi)存用量是影響性能的重要因素,你就應(yīng)當(dāng)盡量去除你不需要的模塊。如果你將模塊編譯成DSO ,取消不必要的模塊就是一件非常簡(jiǎn)單的事情:注釋掉LoadModule指令中不需要的模塊。

如果你已經(jīng)將模塊靜態(tài)鏈接進(jìn)Apache二進(jìn)制核心,你就必須重新編譯Apache并去掉你不想要的模塊。

增減模塊牽涉到的一個(gè)問題是:究竟需要哪些模塊、不需要哪些模塊?這取決于服務(wù)器的具體情況。一般說來,至少要包含下列模塊:mod_mime, mod_dir, mod_log_config 。你也可以不要mod_log_config ,但是一般不推薦這樣做。
原子操作

一些模塊,比如mod_cache和worker使用APR(Apache可移植運(yùn)行時(shí))的原子API。這些API提供了能夠用于輕量級(jí)線程同步的原子操作。

默認(rèn)情況下,APR在每個(gè)目標(biāo)OS/CPU上使用其最有效的特性執(zhí)行這些操作。比如許多現(xiàn)代CPU的指令集中有一個(gè)原子的比較交換(compare-and -swap, CAS)操作指令。在一些老式平臺(tái)上,APR默認(rèn)使用一種緩慢的、基于互斥執(zhí)行的原子API以保持對(duì)沒有CAS指令的老式CPU的兼容。如果你只打算在新式的CPU上運(yùn)行Apache,你可以在編譯時(shí)使用 --enable-nonportable-atomics 選項(xiàng):

./buildconf
./configure --with-mpm=worker --enable-nonportable-atomics=yes

--enable-nonportable-atomics 選項(xiàng)只和下列平臺(tái)相關(guān):

  * SPARC上的Solaris
    默認(rèn)情況下,APR使用基于互斥執(zhí)行的原子操作。如果你使用 --enable-nonportable-atomics 選項(xiàng),APR將使用SPARC v8plus操作碼來加快基于硬件的CAS操作。注意,這僅對(duì)UltraSPARC CPU有效。
  * x86上的Linux
    默認(rèn)情況下,APR在Linux上使用基于互斥執(zhí)行的原子操作。如果你使用 --enable-nonportable-atomics 選項(xiàng),APR將使用486操作碼來加快基于硬件的CAS操作。注意,這僅對(duì)486以上的CPU有效。

mod_status 和 "ExtendedStatus On"

如果Apache在編譯時(shí)包含了mod_status ,而且在運(yùn)行時(shí)設(shè)置了"ExtendedStatus On",那么Apache會(huì)對(duì)每個(gè)請(qǐng)求調(diào)用兩次gettimeofday()(或者根據(jù)操作系統(tǒng)的不同,調(diào)用times())以及(1.3版之前)幾個(gè)額外的time()調(diào)用,使?fàn)顟B(tài)記錄帶有時(shí)間標(biāo)志。為了得到最佳性能,可以設(shè)置"ExtendedStatus off"(這也是默認(rèn)值)。
多socket情況下的串行accept
警告

這部分內(nèi)容尚未完全根據(jù)Apache2.0中的變化進(jìn)行更新 。一些信息依然有效,使用中請(qǐng)注意。

這里要說的是 Unix socket API 的一個(gè)缺點(diǎn)。假設(shè)web服務(wù)器使用了多個(gè)Listen語句****多個(gè)端口或者多個(gè)地址,Apache會(huì)使用select()以檢測(cè)每個(gè)socket是否就緒。select()會(huì)表明一個(gè)socket有零或至少一個(gè)連接正等候處理。由于Apache的模型是多子進(jìn)程的,所有空閑進(jìn)程會(huì)同時(shí)檢測(cè)新的連接。一個(gè)很天真的實(shí)現(xiàn)方法是這樣的(這些例子并不是源代碼,只是為了說明問題而已):

for (;;) {
for (;;) {
fd_set accept_fds;

FD_ZERO (&accept_fds);
for (i = first_socket; i = last_socket; ++i) {
FD_SET (i, &accept_fds);
}
rc = select (last_socket+1, &accept_fds, NULL, NULL, NULL);
if (rc 1) continue;
new_connection = -1;
for (i = first_socket; i = last_socket; ++i) {
if (FD_ISSET (i, &accept_fds)) {
new_connection = accept (i, NULL, NULL);
if (new_connection != -1) break;
}
}
if (new_connection != -1) break;
}
process the new_connection;
}

這種天真的實(shí)現(xiàn)方法有一個(gè)嚴(yán)重的"饑餓"問題。如果多個(gè)子進(jìn)程同時(shí)執(zhí)行這個(gè)循環(huán),則在多個(gè)請(qǐng)求之間,進(jìn)程會(huì)被阻塞在select ,隨即進(jìn)入循環(huán)并試圖accept此連接,但是只有一個(gè)進(jìn)程可以成功執(zhí)行(假設(shè)還有一個(gè)連接就緒),而其余的則會(huì)被阻塞在accept 。這樣,只有那一個(gè)socket可以處理請(qǐng)求,而其他都被鎖住了,直到有足夠多的請(qǐng)求將它們喚醒。此"饑餓"問題在PR#467中有專門的講述。目前至少有兩種解決方案。

一種方案是使用非阻塞型socket ,不阻塞子進(jìn)程并允許它們立即繼續(xù)執(zhí)行。但是這樣會(huì)浪費(fèi)CPU時(shí)間。設(shè)想一下,select有10個(gè)子進(jìn)程,當(dāng)一個(gè)請(qǐng)求到達(dá)的時(shí)候,其中9個(gè)被喚醒,并試圖accept此連接,繼而進(jìn)入select循環(huán),無所事事,并且其間沒有一個(gè)子進(jìn)程能夠響應(yīng)出現(xiàn)在其他socket上的請(qǐng)求,直到退出select循環(huán)??傊@個(gè)方案效率并不怎么高,除非你有很多的CPU,而且開了很多子進(jìn)程。

另一種也是Apache所使用的方案是,使內(nèi)層循環(huán)的入口串行化,形如(不同之處以高亮顯示):

for (;;) {
accept_mutex_on ();
for (;;) {
fd_set accept_fds;

FD_ZERO (&accept_fds);
for (i = first_socket; i = last_socket; ++i) {
FD_SET (i, &accept_fds);
}
rc = select (last_socket+1, &accept_fds, NULL, NULL, NULL);
if (rc 1) continue;
new_connection = -1;
for (i = first_socket; i = last_socket; ++i) {
if (FD_ISSET (i, &accept_fds)) {
new_connection = accept (i, NULL, NULL);
if (new_connection != -1) break;
}
}
if (new_connection != -1) break;
}
accept_mutex_off ();
process the new_connection;
}

函數(shù)accept_mutex_on和accept_mutex_off實(shí)現(xiàn)了一個(gè)互斥信號(hào)燈,在任何時(shí)刻只被為一個(gè)子進(jìn)程所擁有。實(shí)現(xiàn)互斥的方法有多種,其定義位于src/conf.h(1.3以前的版本)或src/include/ap_config.h(1.3或以后的版本)中。在一些根本沒有鎖定機(jī)制的體系中,使用多個(gè)Listen指令就是不安全的。

AcceptMutex指令被用來改變?cè)谶\(yùn)行時(shí)使用的互斥方案。

AcceptMutex flock

  這種方法調(diào)用系統(tǒng)函數(shù)flock()來鎖定一個(gè)加鎖文件(其位置取決于LockFile指令)。
AcceptMutex fcntl

  這種方法調(diào)用系統(tǒng)函數(shù)fcntl()來鎖定一個(gè)加鎖文件(其位置取決于LockFile指令)。
AcceptMutex sysvsem

  (1.3及更新版本)這種方案使用SysV風(fēng)格的信號(hào)燈以實(shí)現(xiàn)互斥。不幸的是,SysV風(fēng)格的信號(hào)燈有一些副作用,其一是,Apache有可能不能在結(jié)束以前釋放這種信號(hào)燈(見ipcs()的man page),另外,這種信號(hào)燈API給與網(wǎng)絡(luò)服務(wù)器有相同uid的CGI提供了拒絕服務(wù)攻擊的機(jī)會(huì)(所有CGI,除非用了類似suexec或cgiwrapper)。鑒于此,在多數(shù)體系中都不用這種方法,除了IRIX(因?yàn)榍皟煞N方法在IRIX中代價(jià)太高)。
AcceptMutex pthread

  (1.3 及更新版本)這種方法使用了POSIX互斥,按理應(yīng)該可以用于所有完整實(shí)現(xiàn)了POSIX線程規(guī)范的體系中,但是似乎只能用在Solaris2.5及更新版本中,甚至只能在某種配置下才正常運(yùn)作。如果遇到這種情況,則應(yīng)該提防服務(wù)器的掛起和失去響應(yīng)。只提供靜態(tài)內(nèi)容的服務(wù)器可能不受影響。
AcceptMutex posixsem

  (2.0及更新版本)這種方法使用了POSIX信號(hào)燈。如果一個(gè)運(yùn)行中的線程占有了互斥segfault ,則信號(hào)燈的所有者將不會(huì)被恢復(fù),從而導(dǎo)致服務(wù)器的掛起和失去響應(yīng)。

如果你的系統(tǒng)提供了上述方法以外的串行機(jī)制,那就可能需要為APR增加代碼(或者提交一個(gè)補(bǔ)丁給Apache)。

還有一種曾經(jīng)考慮過但從未予以實(shí)施的方案是使循環(huán)部分地串行化,即只允許一定數(shù)量的進(jìn)程進(jìn)入循環(huán)。這種方法僅在多個(gè)進(jìn)程可以同時(shí)進(jìn)行的多處理器的系統(tǒng)中才是有價(jià)值的,而且這樣的串行方法并沒有占用整個(gè)帶寬。它也許是將來研究的一個(gè)領(lǐng)域,但是由于高度并行的網(wǎng)絡(luò)服務(wù)器并不符合規(guī)范,所以其被優(yōu)先考慮的程度會(huì)比較低。

當(dāng)然,為了得到最佳性能,最后就根本不使用多個(gè)Listen語句。但是上述內(nèi)容還是值得讀一讀。
單socket情況下的串行accept

上述對(duì)多socket的服務(wù)器進(jìn)行了一流的講述,那么對(duì)單socket的服務(wù)器又怎樣呢?理論上似乎應(yīng)該沒有什么問題,因?yàn)樗羞M(jìn)程在連接到來的時(shí)候可以由accept()阻塞,而不會(huì)產(chǎn)生進(jìn)程"饑餓"的問題,但是在實(shí)際應(yīng)用中,它掩蓋了與上述非阻塞方案幾乎相同的問題。按大多數(shù)TCP棧的實(shí)現(xiàn)方法,在單個(gè)連接到來時(shí),內(nèi)核實(shí)際上喚醒了所有阻塞在accept的進(jìn)程,但只有一個(gè)能得到此連接并返回到用戶空間,而其余的由于得不到連接而在內(nèi)核中處于休眠狀態(tài)。這種休眠狀態(tài)為代碼所掩蓋,但的確存在,并產(chǎn)生與多socket中采用非阻塞方案相同的負(fù)載尖峰的浪費(fèi)。

同時(shí),我們發(fā)現(xiàn)在許多體系結(jié)構(gòu)中,即使在單socket的情況下,實(shí)施串行化的效果也不錯(cuò),因此在幾乎所有的情況下,事實(shí)上就都這樣處理了。在Linux (2.0.30,雙Pentium pro 166/128M RAM)下的測(cè)試顯示,對(duì)單socket,串行化比不串行化每秒鐘可以處理的請(qǐng)求少了不到3%,但是,不串行化對(duì)每一個(gè)請(qǐng)求多了額外的100ms的延遲,此延遲可能是因?yàn)殚L(zhǎng)距離的網(wǎng)絡(luò)線路所致,并且僅發(fā)生在LAN中。如果需要改變對(duì)單socket的串行化,可以定義SINGLE_LISTEN_UNSERIALIZED_ACCEPT ,使單socket的服務(wù)器徹底放棄串行化。
延遲的關(guān)閉

正如draft-ietf-http-connection-00.txt section 8所述,HTTP服務(wù)器為了可靠地實(shí)現(xiàn)此協(xié)議,需要單獨(dú)地在每個(gè)方向上關(guān)閉通訊(重申一下,一個(gè)TCP連接是雙向的,兩個(gè)方向之間是獨(dú)立的)。在這一點(diǎn)上,其他服務(wù)器經(jīng)常敷衍了事,但從1.2版本開始被Apache正確實(shí)現(xiàn)了。

但是增加了此功能以后,由于一些Unix版本的短見,隨之也出現(xiàn)了許多問題。TCP規(guī)范并沒有規(guī)定FIN_WAIT_2必須有一個(gè)超時(shí),但也沒有明確禁止。在沒有超時(shí)的系統(tǒng)中,Apache1.2經(jīng)常會(huì)陷于FIN_WAIT_2狀態(tài)中。多數(shù)情況下,這個(gè)問題可以用供應(yīng)商提供的TCP/IP補(bǔ)丁予以解決。而如果供應(yīng)商不提供補(bǔ)丁(指SunOS4 -- 盡管用戶們持有允許自己修補(bǔ)代碼的許可證),那么只能關(guān)閉此功能。

實(shí)現(xiàn)的方法有兩種,其一是socket選項(xiàng)SO_LINGER ,但是似乎命中注定,大多數(shù)TCP/IP棧都從未予以正確實(shí)現(xiàn)。即使在正確實(shí)現(xiàn)的棧中(指Linux2.0.31),此方法也被證明其代價(jià)比下一種方法高昂。

Apache對(duì)此的實(shí)現(xiàn)代碼大多位于函數(shù)lingering_close(位于http_main.c)中。此函數(shù)大致形如:

void lingering_close (int s)
{
char junk_buffer[2048];

/* shutdown the sending side */
shutdown (s, 1);

signal (SIGALRM, lingering_death);
alarm (30);

for (;;) {
select (s for reading, 2 second timeout);
if (error) break;
if (s is ready for reading) {
if (read (s, junk_buffer, sizeof (junk_buffer)) = 0) {
break;
}
/* just toss away whatever is here */
}
}

close (s);
}

此代碼在連接結(jié)束時(shí)多了一些開銷,但這是可靠實(shí)現(xiàn)所必須的。由于HTTP/1.1越來越流行,而且所有連接都是穩(wěn)定的,此開銷將由更多的請(qǐng)求共同分擔(dān)。如果你要玩火去關(guān)閉這個(gè)功能,可以定義NO_LINGCLOSE ,但絕不推薦這樣做。尤其是,隨著HTTP/1.1中管道化穩(wěn)定連接的啟用,lingering_close已經(jīng)成為絕對(duì)必須。而且,管道化連接速度更快,應(yīng)該考慮予以支持。
Scoreboard 文件

Apache父進(jìn)程和子進(jìn)程通過scoreboard進(jìn)行通訊。通過共享內(nèi)存來實(shí)現(xiàn)當(dāng)然是最理想的。在我們?cè)?jīng)實(shí)踐過或者提供了完整移植的操作系統(tǒng)中,都使用共享內(nèi)存,其余的則使用磁盤文件。磁盤文件不僅速度慢,而且不可靠(功能也少)。仔細(xì)閱讀你的體系所對(duì)應(yīng)的src/main/conf.h文件,并查找USE_MMAP_SCOREBOARD或USE_SHMGET_SCOREBOARD 。定義其中之一(或者分別類似HAVE_MMAP和HAVE_SHMGET),可以使共享內(nèi)容的相關(guān)代碼生效。如果你的系統(tǒng)提供其他類型的共享內(nèi)容,則需要修改src/main/http_main.c文件,并把必需的掛鉤添加到服務(wù)器中。(也請(qǐng)發(fā)送一個(gè)補(bǔ)丁給我們)
注意:在對(duì)Linux的Apache1.2移植版本之前,沒有使用內(nèi)存共享,此失誤使Apache的早期版本在Linux中表現(xiàn)很差。
DYNAMIC_MODULE_LIMIT

如果你不想使用動(dòng)態(tài)加載模塊(或者是因?yàn)榭匆娏诉@段話,或者是為了獲得最后一點(diǎn)點(diǎn)性能上的提高),可以在編譯服務(wù)器時(shí)定義 -DDYNAMIC_MODULE_LIMIT=0 ,這樣可以節(jié)省為支持動(dòng)態(tài)加載模塊而分配的內(nèi)存。
top
附錄:蹤跡的詳細(xì)分析

在Solaris8的MPM中,Apache2.0.38使用一個(gè)系統(tǒng)調(diào)用以收集蹤跡:

truss -l -p httpd_child_pid.

-l 參數(shù)使truss記錄每個(gè)執(zhí)行系統(tǒng)調(diào)用的LWP(lightweight process--Solaris核心級(jí)線程)的ID。

其他系統(tǒng)可能使用不同的系統(tǒng)調(diào)用追蹤工具,諸如strace, ktrace, par ,其輸出都是相似的。

下例中,一個(gè)客戶端向httpd請(qǐng)求了一個(gè)10KB的靜態(tài)文件。對(duì)非靜態(tài)或內(nèi)容協(xié)商請(qǐng)求的記錄會(huì)有很大不同(有時(shí)也很難看明白)。

/67:   accept(3, 0x00200BEC, 0x00200C0C, 1) (sleeping...)
/67:   accept(3, 0x00200BEC, 0x00200C0C, 1)         = 9

下例中,****線程是 LWP #67 。
注意對(duì)accept()串行化支持的匱乏。與這個(gè)特殊平臺(tái)對(duì)應(yīng)的MPM在默認(rèn)情況下使用非串行的accept ,除了在****多個(gè)端口的時(shí)候。

/65:   lwp_park(0x00000000, 0)                 = 0
/67:   lwp_unpark(65, 1)                     = 0

接受了一個(gè)連接后,****線程喚醒一個(gè)工作線程以處理此請(qǐng)求。下例中,處理請(qǐng)求的那個(gè)工作線程是 LWP #65 。

/65:   getsockname(9, 0x00200BA4, 0x00200BC4, 1)     = 0

為了實(shí)現(xiàn)虛擬主機(jī),Apache需要知道接受連接的本地socket地址。在許多情況下,有可能無須執(zhí)行此調(diào)用(比如沒有虛擬主機(jī),或者Listen指令中沒有使用通配地址),但是目前并沒有對(duì)此作優(yōu)化處理。

/65:   brk(0x002170E8)                       = 0
/65:   brk(0x002190E8)                       = 0

此brk()調(diào)用是從堆中分配內(nèi)存的,它在系統(tǒng)調(diào)用記錄中并不多見,因?yàn)閔ttpd在多數(shù)請(qǐng)求處理中使用了自己的內(nèi)存分配器(apr_pool和apr_bucket_alloc)。下例中,httpd剛剛啟動(dòng),所以它必須調(diào)用malloc()以分配原始內(nèi)存塊用于自己的內(nèi)存分配器。

/65:   fcntl(9, F_GETFL, 0x00000000)             = 2
/65:   fstat64(9, 0xFAF7B818)                 = 0
/65:   getsockopt(9, 65535, 8192, 0xFAF7B918, 0xFAF7B910, 2190656) = 0
/65:   fstat64(9, 0xFAF7B818)                 = 0
/65:   getsockopt(9, 65535, 8192, 0xFAF7B918, 0xFAF7B914, 2190656) = 0
/65:   setsockopt(9, 65535, 8192, 0xFAF7B918, 4, 2190656) = 0
/65:   fcntl(9, F_SETFL, 0x00000082)             = 0

接著,工作線程使客戶端連接處于非阻塞模式。setsockopt()和getsockopt()調(diào)用是Solaris的libc對(duì)socket執(zhí)行fcntl()所必須的。

/65:   read(9, " G E T   / 1 0 k . h t m".., 8000)   = 97

工作線程從客戶端讀取請(qǐng)求。

/65:   stat("/var/httpd/apache/httpd-8999/htdocs/10k.html", 0xFAF7B978) = 0
/65:   open("/var/httpd/apache/httpd-8999/htdocs/10k.html", O_RDONLY) = 10

這里,httpd被配置為"Options FollowSymLinks"和"AllowOverride None"。所以,無須對(duì)每個(gè)被請(qǐng)求文件路徑中的目錄執(zhí)行l(wèi)stat(),也不需要檢查.htaccess文件,它簡(jiǎn)單地調(diào)用stat()以檢查此文件是否存在,以及是一個(gè)普通的文件還是一個(gè)目錄。

/65:   sendfilev(0, 9, 0x00200F90, 2, 0xFAF7B53C)     = 10269

此例中,httpd可以通過單個(gè)系統(tǒng)調(diào)用sendfilev()發(fā)送HTTP響應(yīng)頭和被請(qǐng)求的文件。Sendfile因操作系統(tǒng)會(huì)有所不同,有些系統(tǒng)中,在調(diào)用sendfile()以前,需要調(diào)用write()或writev()以發(fā)送響應(yīng)頭。

/65:   write(4, " 1 2 7 . 0 . 0 . 1   - ".., 78)     = 78

此write()調(diào)用在訪問日志中對(duì)請(qǐng)求作了記錄。注意,其中沒有對(duì)time()的調(diào)用的記錄。與Apache1.3不同,Apache2.0使用gettimeofday()以查詢時(shí)間。在有些操作系統(tǒng)中,比如Linux和Solaris,gettimeofday有一個(gè)優(yōu)化的版本,其開銷比一個(gè)普通的系統(tǒng)調(diào)用要小一點(diǎn)。

/65:   shutdown(9, 1, 1)                     = 0
/65:   poll(0xFAF7B980, 1, 2000)               = 1
/65:   read(9, 0xFAF7BC20, 512)                 = 0
/65:   close(9)                           = 0

工作線程對(duì)連接作延遲的關(guān)閉。

/65:   close(10)                           = 0
/65:   lwp_park(0x00000000, 0)       (sleeping...)

最后,工作線程關(guān)閉發(fā)送完的文件和塊,直到****進(jìn)程把它指派給另一個(gè)連接。

/67:   accept(3, 0x001FEB74, 0x001FEB94, 1) (sleeping...)

其間,****進(jìn)程可以在把一個(gè)連接指派給一個(gè)工作進(jìn)程后立即接受另一個(gè)連接(但是如果所有工作進(jìn)程都處于忙碌狀態(tài),則會(huì)受MPM中的一些溢出控制邏輯的制約)。雖然在此例中并不明顯,在工作線程剛接受了一個(gè)連接之后,下一個(gè)accept()會(huì)(在高負(fù)荷的情況下更會(huì))立即并行產(chǎn)生。

標(biāo)簽:十堰 人事邀約 漳州 邵陽 企業(yè)管理 泰安 甘孜 撫順

巨人網(wǎng)絡(luò)通訊聲明:本文標(biāo)題《APACHE性能方面的提示》,本文關(guān)鍵詞  APACHE,性能,方面的,方,面的,;如發(fā)現(xiàn)本文內(nèi)容存在版權(quán)問題,煩請(qǐng)?zhí)峁┫嚓P(guān)信息告之我們,我們將及時(shí)溝通與處理。本站內(nèi)容系統(tǒng)采集于網(wǎng)絡(luò),涉及言論、版權(quán)與本站無關(guān)。
  • 相關(guān)文章
  • 下面列出與本文章《APACHE性能方面的提示》相關(guān)的同類信息!
  • 本頁收集關(guān)于APACHE性能方面的提示的相關(guān)信息資訊供網(wǎng)民參考!
  • 推薦文章