服務器的性能指標

服務器的性能指標

    
作爲一個網絡服務器程序,性能永遠是第一位的指標。性能可以這樣定義:在給定的硬件條件和時間裏,能夠處理的任務量。能夠最大限度地利用硬件性能的服務器設計纔是良好的設計。
    
設計良好的服務器還應該考慮平均服務,對於每一個客戶端,服務器應該給予每個客戶端平均的服務,不能讓某一個客戶端長時間得不到服務而發生飢餓的狀況。
    
可伸縮性,也就是說,隨着硬件能力的提高,服務器的性能能夠隨之呈線性增長。

實現高性能的途徑

    
一個實際的服務器的計算是很複雜的,往往是混合了IO計算和CPU計算。IO計算指計算任務中以IO爲主的計算模型,比如文件服務器、郵件服務器等,混合了大量的網絡IO和文件IOCPU計算指計算任務中沒有或很少有IO,比如加密/解密,編碼/解碼,數學計算等等。
    
CPU計算中,單 線程和多線程模型效果是相當的。《Win32多線程的性能》中說在一個單處理器的計算機中,基於 CPU 的任務的併發執行速度不可能比串行執行速度快,但是我們可以看到,在 Windows NT 下線程創建和切換的額外開銷非常小;對於非常短的計算,併發執行僅僅比串行執行慢 10%,而隨着計算長度的增加,這兩個時間就非常接近了。
    
可見,對於純粹的CPU計算來說,如果只有一個CPU,多線程模型是不合適的。考慮一個執行密集的CPU計算的服務,如果有幾十個這樣的線程併發執行,過於頻繁地任務切換導致了不必要的性能損失。

    
在編程實現上,單線程模型計算模型對於服務器程序設計是很不方便的。因此,對於CPU計算採用線程池工作模型是比較恰當的。 QueueUserWorkItem函數非常適合於將一個CPU計算放入線程池。線程池實現將會努力減少這種不必要的線程切換,而且控制併發線程的數目爲 CPU的數目。
    
我們真正需要關心的是IO計算,一般的網絡服務器程序往往伴隨着大量的IO計算。提高性能的途徑在於要避免等待IO 的結束,造成CPU空閒,要儘量利用硬件能力,讓一個或多個IO設備與CPU併發執行。前面介紹的異步IOAPCIO完成端口都可以達到這個目的。
    
對於網絡服務器來說,如果客戶端併發請求數目比較少的話,用簡單的多線程模型就可以應付了。如果一個線程因爲等待IO操作完成而被掛起,操作系統將會調度 另外一個就緒的線程投入運行,從而形成併發執行。經典的網絡服務器邏輯大多采用多線程/多進程方式,在一個客戶端發起到服務器的連接時,服務器將會創建一 個線程,讓這個新的線程來處理後續事務。這種以一個專門的線程/進程來代表一個客戶端對象的編程方法非常直觀,易於理解。
    
對於大型網絡服 務器程序來說,這種方式存在着侷限性。首先,創建線程/進程和銷燬線程/進程的代價非常高昂,尤其是在服務器採用TCP“短連接方式或UDP方式通訊的情況下,例如,HTTP協議中,客戶端發起一個連接後,發送一個請求,服務器迴應了這個請求後,連接也就被關閉了。如果採用經典方式設計HTTP服務器, 那麼過於頻繁地創建線程/銷燬線程對性能造成的影響是很惡劣的。
    
其次,即使一個協議中採取TCP“長連接,客戶端連上服務器後就一直保 持此連接,經典的設計方式也是有弊病的。如果客戶端併發請求量很高,同一時刻有很多客戶端等待服務器響應的情況下,將會有過多的線程併發執行,頻繁的線程切換將用掉一部分計算能力。實際上,如果併發線程數目過多的話,往往會過早地耗盡物理內存,絕大部分時間耗費在線程切換上,因爲線程切換的同時也將引起內 存調頁。最終導致服務器性能急劇下降,
    
對於一個需要應付同時有大量客戶端併發請求的網絡服務器來說,線程池是唯一的解決方案。線程池不光能夠避免頻繁地創建線程和銷燬線程,而且能夠用數目很少的線程就可以處理大量客戶端併發請求。
    
值得注意的是,對於一個壓力不大的網絡服務器程序設計,我們並不推薦以上任何技巧。在簡單的設計就能夠完成任務的情況下,把事情弄得很複雜是很不明智,很愚蠢的行爲。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章