提升服务器性能的建议
性能對于服務器來說是至關重要的,畢竟每個活動都期望起請求能很快的得到響應.影響服務器性能的首要因素就是系統的硬件資源.比如CPU的個數 速度 內存的大小等.不過由于硬件技術的飛速發展,現代服務器都不缺反硬件資源,因此我們需要考慮的主要問題是如何從"軟環境"來提升服務器的性能.服務器的"軟環境",一方面是指系統的軟件資源,比如操作系統允許用戶打開的最大文件描述符數量;另一方面指的就是服務器程序本身,即如何從編程的調度確保服務器的性能.
下面我們進一步分析高性能服務器需要注意的其他幾個方面:池 數據復制 上下文切換和鎖.
1.池
既然服務器的硬件資源"充裕",那么提高服務器性能的一個很直接的方法就是以空間換時間,即浪費服務器的硬件資源以換取其運行效率.這就是池的概念.池是一組資源的集合,這組資源在服務器啟動之初就被完全創建好并初始化,這成為靜態資源分配.當服務器進入正式運行階段,即開始處理客戶請求的時候,如果它需要相關的資源,就可以直接從池中獲取,無須動態分配.很顯然,直接從池中取得所需資源比動態分配資源的速度要快很多.因為分配系統資源的系統調用都是很耗時的.當服務器處理完一個客戶連接后,可以吧相關的資源放回池中,無須執行系統調用來釋放資源.從最終的效果來看,池相當于服務器管理系統資源的應用層設施,它避免了服務器對內核的頻繁訪問.
不過,既然池中的資源是預先靜態分配的,我們就無法預期應該分配多少資源.這個問題又該如何解決?最簡單的解決方法就是分配足夠多的資源,即針對每個可能的客戶連接都分配必要的資源,這通常會導致資源的浪費,因為任一時刻的客戶數量都可能遠遠沒有達到服務器能支持的最大客戶數量.好在這種資源的浪費對于服務器來說不會構成問題.還有一種解決方法就是預先分配一定的資源,此后如果發現資源不夠用,就再動態分配一些并加入翅中.
根據不同的資源類型,可以吧池分別多種.常見的有內存池,進程池,線程池和連接池.
內存池通常用于socket的接收緩存和發送緩存.對于某些長度優先的客戶請求,比如http請求,預先分配一個大小足夠的接收緩沖區是合理的.當客戶請求的長度超過接收緩沖區的大小時,我們可以選擇丟棄請求或動態擴大接收緩沖區.
進程池和線程池都是并發常用的.當我們需要一個工作線程或工作進程來處理新到來的客戶請求時,我們可以直接從進程池和線程池取得一個執行實體,而無需動態調用fork或pthread_creat來創建進程和線程.
連接池通常用戶服務器或服務器集群的內部永久連接.
在圖8-4中,每個邏輯單元可能都需要平凡的訪問本地的某個數據庫.簡單的做法:邏輯單元每次需要訪問數據庫的時候就想數據庫程序發起連接,而訪問完畢后釋放連接.很顯然,這樣做法效率太低.一種解決方法就是使用連接池,連接池的服務器預先和數據庫程序建立的一組連接的集合.當某個邏輯單元需要訪問數據庫時,它可以直接從連接池中取得一個連接的實體并使用之.待完成數據庫的訪問之后,邏輯單元再講該鏈接返回給連接池.
2.數據復制
高性能服務器應該避免不必要的數據復制.尤其是當數據復制發送在用戶代碼和內核之間的時候.如果內核可以直接從socket或文件讀取的時候,則應用程序就沒必要從內核緩沖區復制到應用程序緩沖區.這里所的直接處理, 指的是應用程序不關心這些數據的內容,不需要對他們做任何分析.比如ftp服務器,當客戶請求一個文件時,服務器只需要檢測目標文件是否存在,以及用戶是否有讀取他的權限,而絕對不關心這個文件的具體內容.這樣的話 ftp服務器無須吧目標文件的內容完整的讀入到應用程序緩沖區中并調用send函數來發送,而是可以使用零拷貝函數(sendfile)來直接將其發送給客戶端.
此外,用戶代碼內部(不可訪問內核)的數據也是可以避免的,舉例來說,當兩個工作進程之間要傳遞大量的數據時,我們就應該考慮使用共享內存來在他們之間共享這些數據,而不是使用管道或者消息隊列來傳遞.
3.上下文切換和鎖
并發程序必須考慮上下文切換的問題,即進程切換和線程切換導致的系統開銷.即使是IO密集型的服務器,也不該使用過多的工作線程,否則線程間的切換將占用大量的cpu時間.服務器真正用于處理業務邏輯的CPU時間的比重就顯得不足了.因此為每個客戶連接都創建一個工作線程的服務器是不可取的.
圖8-11描述的半同步/半異步模式是一種比較合理的解決方案.它允許一個線程同時處理多個客戶連接.此外,多線程服務器的一個優點是不同的線程可以同步運行在不同的CPU上,當線程的數量不大于CPU的數量是,上下文的切換就不是問題了.
總結
以上是生活随笔為你收集整理的提升服务器性能的建议的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: UML大战需求分析--阅读笔记02
- 下一篇: webform(八)——LinQ简单增、