先後查看了haproxy,l7sw和lighttpd的 相關源碼,無一例外,他們一致認爲多路複用是性能最好的服務器架構。事實也確實應該如此,進程的出現一方面就是爲了保存任務的執行上下文從而簡化應用程序 設計,如果程序的邏輯結構不是很複雜,那麼用整個進程控制塊來保存執行上下文未免有些大材小用,加上進程調度和其他的一些額外開銷,程序設計上的高效很可 能會被執行時的低效所抵消。代價也是有的:程序設計工作將更加具有挑戰性。
體系結構選定之後,我們就要考慮更加細節的部分,比如說用什麼操作系統,用操作系統提供的那些API。在這方面,前輩們已經做過很多,我們只需要簡單的“拿來”即可,如果再去枉費脣舌,簡直就是浪費時間,圖財害命。High-Performance Server Architecture從根本上分析了導致服務器低效的罪魁禍首:數據拷貝、(用戶和內核)上下文切換、內存申請(管理)和鎖競爭;The C10K Problem列舉並分析了UNIX、Linux甚至是部分Windows爲提高服務器性能而設計的一些系統調用接口,這篇文檔的難能可貴之處還在於它一致保持更新;Benchmarking BSD and Linux更是通過實測數據用圖表的形式把BSD和Linux的相關係統調用的性能直觀地陳列在我們眼前,結果還是令人激動的:Linux 2.6的相關係統調用的時間複雜度竟然是O(1)。
簡單的總結如下:
- 操作系統採用Linux 2.6.x內核,不僅因爲它的高性能,更因爲它大開源(這並不是說其他的UNIX或者是BSD衍生物不開源)給程序設計帶來的便利,我們甚至可以把服務做到內核空間。
- 多路複用採用epoll的“電平觸發”(Level Triggered)模式,必要時可以採用“邊緣觸發”(Edge Triggered),但要注意防止數據停滯。
- 爲避免數據拷貝可以採用sendfile系統調用發送小文件,或者是文件的小部分,注意避免sendfile因磁盤IO而導致的阻塞。
- 如果服務操作設計大量磁盤IO操作,應選用Linux內核提供的異步IO機制,其對應的用戶空間庫爲libaio,注意:這裏提到異步IO庫並非目前glibc中附帶的異步IO實現。
- 如果同時有多個數據需要傳輸,採用writev/readv來減少系統調用所帶來的上下文切換開銷,如果數據要寫到網絡套接字文件描述符,這也能在一定程度上防止網絡上出現比較小幀,爲此,還可以有選擇地開啓TCP_CORK選項。
- 實現自己的內存管理,比如說緩存數據,複用常用數據結構等。
- 用多線程替代多進程,線程庫當然選擇nptl。
- 避免進程/線程間非必要的同步,保持互斥區的短小。
有一點需要提醒一下,目前SMP系統和多核心CPU比 較常見,如果還是僅採用單進程(線程)的多路複用模型,那麼同一時間將只有一個CPU爲這個進程(線程)服務,並不能充分發揮CPU的計算能力,所以需要 至少CPU(CPU核心)數目個進程(線程)來分擔系統負擔。有一個變通的解決方案:不用修改源碼,在服務器上運行兩個服務程序的實例,當然這個時候服務 端口應該是不同的,然後在其前端放置負載均衡器將流量和連接平均分配到兩個服務端口,可以簡單的通過DNAT來實現負載均衡。其實,這個時候我們已經把多 CPU或者是多核系統看成了多個系統組成的集羣。
爲了提高服務器的性能,單純的依靠提高單個服務器的處理能力似乎不能奏效,況且配置越高 的服務器花銷也就越高,爲此人們經常採用服務器集羣的方式,通過把計算儘可能地分配到相對比較廉價的機器上單獨完成,籍此來提升服務器的整體性能,事實證 明,這種體系結構不僅是切實可行的,而且還能提高服務器的可用性,容錯能力也較強。在網絡服務器方面,Linux內核中的由國人章文嵩先生設計的IP層負載均衡解決方案LVS比較有名,還有就是工作於應用層的haproxy和剛剛起步的l7sw。
發佈於2007年11月09日 21:46 | 評論數(0) 閱讀數(603)
Track back:http://blog.ci123.com/wobushizhanghua/entry/246311