為什么Nginx的性能要比Apache高得多?
這主要是因?yàn)镹ginx使用了最新的epoll(Linux 2.6內(nèi)核)和kqueue(FreeBSD)網(wǎng)絡(luò)I/O模型,而Apache則使用的是傳統(tǒng)的select模型。曾在一篇博客上看到有這么個(gè)實(shí)例:
假設(shè)你在大學(xué)中讀書(shū),要等待一個(gè)朋友來(lái)訪,而這個(gè)朋友只知道你在A號(hào)樓,但是不知道你具體住在哪里,于是你們約好了在A號(hào)樓門口見(jiàn)面.如果你使用的阻塞IO 模型來(lái)處理這個(gè)問(wèn)題,那么你就只能一直守候在A號(hào)樓門口等待朋友的到來(lái),在這段時(shí)間里你不能做別的事情,不難知道,這種方式的效率是低下的.現(xiàn)在時(shí)代變化了,開(kāi)始使用多路復(fù)用IO模型來(lái)處理這個(gè)問(wèn)題.你告訴你的朋友來(lái)了A號(hào)樓找樓管大媽,讓她告訴你該怎么走.這里的樓管大媽扮演的就是多路復(fù)用IO的角色。
解釋select和epoll模型的工作方式:
select版大媽做的是如下的事情:比如同學(xué)甲的朋友來(lái)了,select版大媽比較笨,她帶著朋友挨個(gè)房間進(jìn)行查詢誰(shuí)是同學(xué)甲,你等的朋友來(lái)了。如果每到來(lái)一個(gè)朋友樓管大媽都要全樓的查詢同學(xué),那么處理的效率必然就低下了,過(guò)不久樓底就有不少的人了。
epoll版大媽就比較先進(jìn)了,她記下了同學(xué)甲的信息,比如說(shuō)他的房間號(hào),那么等同學(xué)甲的朋友到來(lái)時(shí),只需要告訴該朋友同學(xué)甲在哪個(gè)房間即可,不用自己親自帶著人滿大樓的找人了。epoll大媽可以不用吹灰之力就可以定位到同學(xué)甲。一看就很明白 epoll和select 模型的區(qū)別了吧。
在Linux內(nèi)核中,select所用到的FD_SET是有限的,即內(nèi)核中有個(gè)參數(shù)__FD_SETSIZE定義了每個(gè)FD_SET的句柄個(gè)數(shù),在內(nèi)核源碼中 /usr/include/linux/posix_types.h 中
#undef __FD_SETSIZE
#define __FD_SETSIZE 1024
如果想要同時(shí)檢測(cè)1025個(gè)句柄的可讀狀態(tài)或 可寫(xiě)狀態(tài) ,select是不能實(shí)現(xiàn)的。在內(nèi)核中實(shí)現(xiàn)select是使用輪詢方法,即每次檢測(cè)都會(huì)遍歷所有FD_SET中的句柄,顯然,select函數(shù)的執(zhí)行時(shí)間與 FD檢測(cè)的句柄數(shù)越多就會(huì)越費(fèi)時(shí)。
epoll是多路復(fù)用IO(I/O Multiplexing) 中的一種方式,僅用于linux2.6以上內(nèi)核。而epoll模型它所支持的FD上限是最大可以打開(kāi)文件的數(shù)目,這個(gè)數(shù)字一般遠(yuǎn)大于2048,舉個(gè)例子,在1GB內(nèi)存的機(jī)器上大約是10萬(wàn)左右,具體請(qǐng)查看:cat /proc/sys/fs/file-max ,這個(gè)數(shù)目和系統(tǒng)內(nèi)存關(guān)系很大。
傳統(tǒng)的select/poll另一個(gè)致命弱點(diǎn)就是當(dāng)你擁有一個(gè)很大的socket集合,不過(guò)由于網(wǎng)絡(luò)延時(shí),任一時(shí)間只有部分的socket是"活躍"的,但是select/poll每次調(diào)用都會(huì)線性掃描全部的集合,導(dǎo)致效率呈現(xiàn)線性下降。但是epoll不存在這個(gè)問(wèn)題,它只會(huì)對(duì)"活躍"的socket進(jìn)行操作---這是因?yàn)樵趦?nèi)核實(shí)現(xiàn)中epoll是根據(jù)每個(gè)fd上面的callback函數(shù)實(shí)現(xiàn)的。那么,只有"活躍"的socket才會(huì)主動(dòng)的去調(diào)用 callback函數(shù),其他idle狀態(tài)socket則不會(huì),在這點(diǎn)上,epoll實(shí)現(xiàn)了一個(gè)"偽"AIO,因?yàn)檫@時(shí)候推動(dòng)力在os內(nèi)核。在一些 benchmark中,如果所有的socket基本上都是活躍的---比如一個(gè)高速LAN環(huán)境,epoll并不比select/poll有什么效率,相反,如果過(guò)多使用epoll_ctl,效率相比還有稍微的下降。但是一旦使用idle connections模擬WAN環(huán)境,epoll的效率就遠(yuǎn)在select/poll之上了。
epoll有兩種工作模式:Edge Triggered (ET)、Level Triggered (LT)
LT(level triggered)是缺省的工作方式,并且同時(shí)支持block和no-block socket.在這種做法中,內(nèi)核告訴你一個(gè)文件描述符是否就緒了,然后可以對(duì)這個(gè)就緒的fd進(jìn)行IO操作。如果你不作任何操作,內(nèi)核還是會(huì)繼續(xù)通知你的,所以,這種模式編程出錯(cuò)誤可能性要小一點(diǎn)。傳統(tǒng)的select/poll都是這種模型的代表。
ET (edge-triggered)是高速工作方式,只支持no-block socket。在這種模式下,當(dāng)描述符從未就緒變?yōu)榫途w時(shí),內(nèi)核通過(guò)epoll告訴你。然后它會(huì)假設(shè)你知道文件描述符已經(jīng)就緒,并且不會(huì)再為那個(gè)文件描述符發(fā)送更多的就緒通知,直到你做了某些操作導(dǎo)致那個(gè)文件描述符不再為就緒狀態(tài)了(比如,你在發(fā)送,接收或者接收請(qǐng)求,或者發(fā)送接收的數(shù)據(jù)少于一定量時(shí)導(dǎo)致了一個(gè)EWOULDBLOCK 錯(cuò)誤)。