服務(wù)器的性能指標(biāo) 作為一個(gè)網(wǎng)絡(luò)服務(wù)器程序,性能永遠(yuǎn)是第一位的指標(biāo)。性能可以這樣定義:在給定的硬件條件和時(shí)間里,能夠處理的任務(wù)量。能夠最大限度地利用硬件性能的服務(wù)器設(shè)計(jì)才是良好的設(shè)計(jì)。 設(shè)計(jì)良好的服務(wù)器還應(yīng)該考慮平均服務(wù),對(duì)于每一個(gè)客戶端,服務(wù)器應(yīng)該給予每個(gè)客戶端平均的服務(wù),不能讓某一個(gè)客戶端長(zhǎng)時(shí)間得不到服務(wù)而發(fā)生“饑餓”的狀況。 可伸縮性,也就是說,隨著硬件能力的提高,服務(wù)器的性能能夠隨之呈線性增長(zhǎng)。 實(shí)現(xiàn)高性能的途徑 一個(gè)實(shí)際的服務(wù)器的計(jì)算是很復(fù)雜的,往往是混合了IO計(jì)算和CPU計(jì)算。IO計(jì)算指計(jì)算任務(wù)中以IO為主的計(jì)算模型,比如文件服務(wù)器、郵件服務(wù)器等,混合了大量的網(wǎng)絡(luò)IO和文件IO;CPU計(jì)算指計(jì)算任務(wù)中沒有或很少有IO,比如加密/解密,編碼/解碼,數(shù)學(xué)計(jì)算等等。 在CPU計(jì)算中,單線程和多線程模型效果是相當(dāng)?shù)?。《Win32多線程的性能》中說“在一個(gè)單處理器的計(jì)算機(jī)中,基于 CPU 的任務(wù)的并發(fā)執(zhí)行速度不可能比串行執(zhí)行速度快,但是我們可以看到,在 Windows NT 下線程創(chuàng)建和切換的額外開銷非常??;對(duì)于非常短的計(jì)算,并發(fā)執(zhí)行僅僅比串行執(zhí)行慢 10%,而隨著計(jì)算長(zhǎng)度的增加,這兩個(gè)時(shí)間就非常接近了。” 可見,對(duì)于純粹的CPU計(jì)算來說,如果只有一個(gè)CPU,多線程模型是不合適的。考慮一個(gè)執(zhí)行密集的CPU計(jì)算的服務(wù),如果有幾十個(gè)這樣的線程并發(fā)執(zhí)行,過于頻繁地任務(wù)切換導(dǎo)致了不必要的性能損失。 在編程實(shí)現(xiàn)上,單線程模型計(jì)算模型對(duì)于服務(wù)器程序設(shè)計(jì)是很不方便的。因此,對(duì)于CPU計(jì)算采用線程池工作模型是比較恰當(dāng)?shù)摹ueueUserWorkItem函數(shù)非常適合于將一個(gè)CPU計(jì)算放入線程池。線程池實(shí)現(xiàn)將會(huì)努力減少這種不必要的線程切換,而且控制并發(fā)線程的數(shù)目為CPU的數(shù)目。 我們真正需要關(guān)心的是IO計(jì)算,一般的網(wǎng)絡(luò)服務(wù)器程序往往伴隨著大量的IO計(jì)算。提高性能的途徑在于要避免等待IO 的結(jié)束,造成CPU空閑,要盡量利用硬件能力,讓一個(gè)或多個(gè)IO設(shè)備與CPU并發(fā)執(zhí)行。前面介紹的異步IO,APC,IO完成端口都可以達(dá)到這個(gè)目的。 對(duì)于網(wǎng)絡(luò)服務(wù)器來說,如果客戶端并發(fā)請(qǐng)求數(shù)目比較少的話,用簡(jiǎn)單的多線程模型就可以應(yīng)付了。如果一個(gè)線程因?yàn)榈却齀O操作完成而被掛起,操作系統(tǒng)將會(huì)調(diào)度另外一個(gè)就緒的線程投入運(yùn)行,從而形成并發(fā)執(zhí)行。經(jīng)典的網(wǎng)絡(luò)服務(wù)器邏輯大多采用多線程/多進(jìn)程方式,在一個(gè)客戶端發(fā)起到服務(wù)器的連接時(shí),服務(wù)器將會(huì)創(chuàng)建一個(gè)線程,讓這個(gè)新的線程來處理后續(xù)事務(wù)。這種以一個(gè)專門的線程/進(jìn)程來代表一個(gè)客戶端對(duì)象的編程方法非常直觀,易于理解。 對(duì)于大型網(wǎng)絡(luò)服務(wù)器程序來說,這種方式存在著局限性。首先,創(chuàng)建線程/進(jìn)程和銷毀線程/進(jìn)程的代價(jià)非常高昂,尤其是在服務(wù)器采用TCP“短連接”方式或UDP方式通訊的情況下,例如,HTTP協(xié)議中,客戶端發(fā)起一個(gè)連接后,發(fā)送一個(gè)請(qǐng)求,服務(wù)器回應(yīng)了這個(gè)請(qǐng)求后,連接也就被關(guān)閉了。如果采用經(jīng)典方式設(shè)計(jì)HTTP服務(wù)器,那么過于頻繁地創(chuàng)建線程/銷毀線程對(duì)性能造成的影響是很惡劣的。 其次,即使一個(gè)協(xié)議中采取TCP“長(zhǎng)連接”,客戶端連上服務(wù)器后就一直保持此連接,經(jīng)典的設(shè)計(jì)方式也是有弊病的。如果客戶端并發(fā)請(qǐng)求量很高,同一時(shí)刻有很多客戶端等待服務(wù)器響應(yīng)的情況下,將會(huì)有過多的線程并發(fā)執(zhí)行,頻繁的線程切換將用掉一部分計(jì)算能力。實(shí)際上,如果并發(fā)線程數(shù)目過多的話,往往會(huì)過早地耗盡物理內(nèi)存,絕大部分時(shí)間耗費(fèi)在線程切換上,因?yàn)榫€程切換的同時(shí)也將引起內(nèi)存調(diào)頁。最終導(dǎo)致服務(wù)器性能急劇下降, 對(duì)于一個(gè)需要應(yīng)付同時(shí)有大量客戶端并發(fā)請(qǐng)求的網(wǎng)絡(luò)服務(wù)器來說,線程池是唯一的解決方案。線程池不光能夠避免頻繁地創(chuàng)建線程和銷毀線程,而且能夠用數(shù)目很少的線程就可以處理大量客戶端并發(fā)請(qǐng)求。 值得注意的是,對(duì)于一個(gè)壓力不大的網(wǎng)絡(luò)服務(wù)器程序設(shè)計(jì),我們并不推薦以上任何技巧。在簡(jiǎn)單的設(shè)計(jì)就能夠完成任務(wù)的情況下,把事情弄得很復(fù)雜是很不明智,很愚蠢的行為。 |
|