1. 程式人生 > >高效能伺服器底層網路通訊模組的設計方法

高效能伺服器底層網路通訊模組的設計方法

  1、概述

  要設計與開發出一款高效能的伺服器(如網遊伺服器、Web伺服器和代理伺服器等),一般都採用高效率的網路I/O模型。Linux平臺上經常會採用epoll模型,而在Win32平臺上完成埠(以下簡稱IOCP)模型是設計與開發高效能的、具有可伸縮性的伺服器的最佳選擇,它可以支援海量併發客戶端請求。多執行緒程式設計是伺服器端開發常用技術,多執行緒必然涉及執行緒間的通訊與同步。如果使用不當,也會影響到系統的效能,必須謹慎設計才能保證系統良好執行。減少資料拷貝以及小物件頻繁建立與銷燬是一種很重要的提高系統性能手段,可通過設計記憶體池或物件池加以解決。這幾個問題的提出,說明實際的高效能伺服器研發比較複雜,尤其是採用高效I/O模型來架構伺服器時,更是增加了開發的難度,原因是這些模型的機制比較複雜。

  底層網路通訊模組是伺服器應用程式的核心模組,也是高效能伺服器的最基礎模組之一。它主要的功能是接收海量併發連線、接收網路資料包、暫存和傳送應用邏輯層的邏輯資料包,所以,它也是上次應用邏輯和底層網路之間通訊的媒介。

  2、 IOCP機制

  要實現一個併發的網路伺服器,比較簡單的模型是:每當一個請求到達就建立一個新執行緒,然後在新執行緒中為請求服務。這種模型減輕了實際開發的複雜度,在併發連線較少的情況下可以考慮使用,然而在高併發需求下並不適用。高併發環境中,建立和銷燬大量執行緒所花費的時間和消耗的系統資源是巨大的,而且會加重執行緒排程的負擔,同時執行緒上下文切換(context switch)也會浪費許多寶貴的CPU時間。

  為了提高系統性能,首先必須有足夠的可執行執行緒來充分利用CPU資源,但執行緒的數量不能太多。事實上,具體工作執行緒的數量和併發連線數量不是直接相關聯的。在Win32平臺下開發高效的伺服器端應用程式,最理想的模型是IOCP模型,該模型解決了一系列系統性能瓶頸問題。

  IOCP提供了最好的可伸縮性,而且其執行效率比較高,採用這種網路模型可能會加大開發的複雜度,但卻是Windows平臺上唯一適用於開發高負載伺服器的技術。IOCPWindows系統的一種核心物件,也是Win32下最複雜的一種I/O模型,它通過一定數量的工作執行緒對重疊I/O請求進行處理,以便為已經完成的I/O請求提供服務,相對其他I/O模型,它可以管理任意數量套接字控制代碼。它主要由等待執行緒佇列和I/O完成佇列2個部分組成。一個完成埠物件可以和多個套接字控制代碼相關聯,當針對某個套接字控制代碼發起的非同步I/O操作完成時,系統向該完成埠的I/O完成佇列加入一個I/O完成包。於此同時,工作執行緒呼叫GetQueuedcompIetionstatus(以下簡稱GQCS)時,如果I/O完成佇列中有完成包,當前呼叫就會返回,取得資料進行後續的處理。

  成功建立一個完成埠後,便可開始將套接字控制代碼與物件關聯到一起。但在關聯套接字之前,首先必須建立一個或多個工作者執行緒為完成埠提供服務。

  3、模組設計方案

  3.1架構設計

  在充分考慮伺服器效能和擴充套件性的基礎上,本文提出了基於三層結構的系統設計方案,該模組的架構如圖1所示。

模組設計架構

  圖1看上去並不複雜,但卻是一個兼顧可擴充套件性和高效能的架構,在實際專案應用中也取得了很好的表現。下面是對圖1主要類的功能分析。

  CIocpServer類足完成埠伺服器基本通訊類,它使用Windows平臺特有的IOCP機制,對網路通訊模型進行底層封裝。提供了基本的伺服器端網路通訊功能,這些功能主要有開啟伺服器、關閉伺服器、管理客戶端連線列表、管理未決的接受請求列表、發出非同步操作等。同時通過多型機制向它的派生類提供以下基本擴充套件介面:

  1.  
    1. 新連線確立的處理介面。
    2. 客戶端斷開連線時的處理介面。
    3. 連接出現錯誤時的處理介面。
    4. 從客戶端接收完資料後的處理介面。
    5. 向客戶端傳送完資料後的處理介面。
    6. 拼包處理介面。

  CUserServer類繼承CIocpServer,在ClocpServer的基礎上,CUserServer加入了一些伺服器邏輯處理功能,並且封裝了3類資料佇列和3類處理執行緒,分別如下:

  (1)接收資料包佇列及接收執行緒:接收佇列用於存放接收到的資料包,此資料包還沒有進行邏輯意義上的拼包,接收執行緒從此佇列中取出資料包,並將其拼裝成邏輯意義上完整的資料包加入到邏輯資料包佇列中。

  (2)邏輯資料包佇列及邏輯處理執行緒:邏輯佇列用於存放已經拼包成了邏輯意義上的資料包,邏輯處理執行緒對此類資料包進行邏輯解析,這裡就是伺服器的主要邏輯部分,有的資料包在處理完成後,可能是需要向客戶端返回處理結果,此時就需要邏輯執行緒將處理完成的資料包放入傳送資料包佇列中。

  (3)傳送資料包佇列及傳送執行緒:傳送佇列存放待發送的資料包,傳送執行緒根據資料包裡的客戶端套接字傳送給特定客戶端。

  CTestServer類是一個測試類,主要用於演示如何在CUserServer的基礎上派生一個真正的應用伺服器,並用於說明它需要過載實現CUserServer的哪些重要虛擬函式。

  3.2資源管理

  用IOCP開發伺服器時,當I/O發生錯誤時需要有效地釋放與套接字相關的快取區,如果對同一快取區釋放多次,就會導致記憶體釋放的錯誤。當投遞的非同步I/O請求返回了非WSA_IO_PENDING錯誤時,要對此錯誤進行處理,通常執行2步操作:釋放此次操作使用的緩衝區資料;關閉當前操作所使用的套接字控制代碼。同時GQCS呼叫會返回FALSE,也要做上面2步相同的操作,這樣就可能產生對同一快取區進行重複釋放的錯誤。解決的辦法可以有2種:

  1.  
    1. 通過引用計數機制控制快取區釋放;
    2. 使快取區釋放操作線性化。

  該系統的沒計採用了第(2)種解決方案,所謂的釋放操作線性化是指把可能引起2次釋放同一快取區的操作合併為一次釋放。如果在執行非同步I/O操作過程中發生了非WSA_IO_PENDING錯誤,可以讓GQCS返回時得知這個錯誤和發生錯誤時的快取區指標,而不對該錯誤進行處理。通知的方式是,使用PostQueuedCompletionStatus(1扶下簡稱Post)函式丟擲一個特殊標誌的訊息,這個特殊標誌可以通過GQCS函式的第2個引數,即傳送位元組數來表示,可以選擇任何一個不可能出現的值,比如一個負數。當然,如果通過單控制代碼資料或單I/O資料來傳遞也是可以的。而發生錯誤時的緩衝區指標,必須要通過單控制代碼資料或單I/O資料來傳遞。

  把釋放操作全放在GQCS函式裡以後,對釋放操作的處理就比較統一了。當然,為了實現真正的線性化和原子化,在釋放操作的執行邏輯上需要對釋放程式碼加鎖以實現執行緒互斥(多執行緒情況下)。

  3.3包的亂序解決方案

  如果在同一個套接字上一次提交多個非同步I/O請求,肯定會按照它們提交的次序完成,但在多執行緒環境下,完成包處理次序可能和提交次序不一致。該問題的一個簡單的解決方法足一次只投遞一個非同步I/O請求,當工作執行緒處理完該請求的完成資料包後,再投遞下一個非同步I/O請求。但這樣做會降低伺服器的處理效能。為了保證完成包處理次序和提交次序相一致,可以為每個連線上投遞的請求都分配一個序號,單控制代碼資料中記錄當前需要讀取的單I/O資料的序號,如果工作執行緒獲得的單I/O資料的序號與單控制代碼資料中記錄的序號一致的話,就處理該資料。如果不相等,則把這個單I/O資料儲存到該連線的pOutOfOrderReads列表中。

4、效能優化

  在網路伺服器的開發過程中,池(Pool)技術已經被廣泛應用。使用池技術在一定層度上可以明顯優化伺服器應用程式的效能,提高程式執行效率和降低系統資源開銷。這裡所說的池是一種廣義上的池,比如資料庫連線池、執行緒池、記憶體池、物件池等。其中,物件池可以看成儲存物件的容器,在程序初始化時建立一定數量的物件。需要時直接從池中取出一個空閒物件,用完後並不直接釋放掉物件,而是再放到物件池中以方便下一次物件請求可以直接複用。其他幾種池的設計思想也是如此,池技術的優勢是,可以消除物件建立所帶來的延遲,從而提高系統的效能。

  4.1執行緒池

  執行緒池是提高伺服器程式效能的一種很好技術,在Win32乎臺下開發的網路伺服器程式使用的執行緒池可分為兩類:一類是由完成埠物件負責維護的工作執行緒池,主要負責網路層相關處理(比如投遞非同步讀或寫操作等);另一類是負責邏輯處理的執行緒池,它是專門提供給應用層來使用的。

  本文提出了一種邏輯執行緒池的設計方案,執行緒池框架結構主要分為以下幾個部分:

  1.  
    1. 執行緒池管理器:用於建立並管理執行緒,往任務佇列新增資料包等,並可以動態增加工作執行緒。
    2. 工作執行緒:執行緒池中的執行緒,執行實際的邏輯處理。
    3. 任務介面:每個任務必須實現的介面,以供工作執行緒排程任務使用。
    4. 任務佇列:提供一種快取機制,用於存放從網路層接收的資料包。

  該通訊模組使用了上述執行緒池的設計方案,從測試結果來看,當併發連線數很大時,執行緒池對伺服器的效能改善是顯著的。

  該設計方案有個很好的特性,就是可以建立工作執行緒數量固定的執行緒池,也可以建立動態執行緒池。如果有大量的客戶要求伺服器為其服務,但由於執行緒池的工作執行緒是有限的話,伺服器只能為部分客戶端服務,客戶端提交的任務只能在任務佇列中等待處理。動態改變的工作執行緒數目的執行緒池,可以以適應突發性的請求。一旦請求變少了將逐步減少執行緒池中工作執行緒的數目。當然執行緒增加可以採用一種超前方式,即批量增加一批工作執行緒,而不是來一個請求才建立建立一個執行緒。批量建立是更加有效的方式,而且該方案還限制了執行緒池中工作執行緒數目的上限和下限,確保執行緒池技術能提高系統整體效能。

  4.2物件池

  物件池是針對特定應用程式而設計的記憶體管理方式,在某種場合下記憶體的分配和釋放效能會大大提升。預設的記憶體管理函式(new/delete或malloc/free)有其不足之處,如果應用程式頻繁地在堆上分配和釋放記憶體,那麼就會導致效能損失,並且會使系統中出現大量的記憶體碎片,降低記憶體的利用率。

  所謂物件池就是應用程式可以通過系統的記憶體分配呼叫預先一次性申請適當大小的記憶體塊,然後可以根據特定物件的大小,把該塊記憶體分割成一個個大小相同的物件。如果物件池中沒有空閒物件使用時,可以再向系統申請同樣大小的記憶體塊。如果物件使用完畢後直接放到物件池中,這種記憶體管理策略能有效地提升程式效能。

  4.2.1 物件池的應用

  當伺服器接受一個客戶端請求後,會建立成功返回一個客戶端套接字控制代碼。如果出現大量併發客戶端連線請求時,就會出現頻繁地分配和釋放物件的情況,這個過程可能會消耗大量的系統資源,有損系統性能。WinSock2還提供一個接受擴充套件函式AcceptEx,它允許在接受連線之前就事先建立一個套接字控制代碼,使之與接受連線相關聯。在呼叫AcceptEx時,可以直接把該控制代碼作為引數傳遞給AcceptEx。有了這個保證,可以通過採用物件池技術來提升系統性能,可以在接受連線之前就建立一定數量的套接字控制代碼,隨著新連線請求的到來將控制代碼分配出去,當客戶端斷開連線後,把相應控制代碼重新放入套接字物件池中。

  另外需要用到物件池的地方是,在每一次投遞WSASend或WSARecv操作時,都要傳進一個重疊結構體引數。可以提前建立一個蕈疊結構體物件池,當發起非同步I/O操作時,先從池中取一個結構體物件,用完之後並不直接銷燬,而是再放回物件池以便以後蘑複利用。建立的結構體數量取決於完成埠的處理效率,如果處理效率比較高,則數量可能就少些,反之,就需要多建立些物件。

  該系統所設計的物件池足執行緒安全的,可以被多個執行緒共享,在獲得和釋放物件時都需要加鎖,從而保證執行緒問互斥訪問物件池。

  4.2.2物件池的優點

  與系統直接管理記憶體相比,物件池在系統性能優化方面主要有如下優點:

  1.  
    1. 針對特殊情況,例如需要頻繁分配和釋放固定大小的物件時,不需要複雜的分配演算法和執行緒同步。也不需要維護記憶體空閒表的額外開銷,從而獲得較好的效能。
    2. 由於直接分配一定數量的連續記憶體空問作為記憶體塊,因此一定程度上提高了程式區域性效能,提升了應用程式整體效能。
    3. 比較容易控制頁邊界對齊和記憶體位元組對齊,基本沒有記憶體碎片問題。

  4.3環形快取區

  基於TCP協議的伺服器應用程式,拼包處理過程必不可少。由於要從接收快取中分解出一個個邏輯資料包,因此一般都要涉及記憶體拷貝操作,過多的記憶體拷貝必然降低系統性能。

  當然,就邏輯資料包的拼裝問題而言,也完全可以避免資料拷貝操作,方法是使用環形緩衝區。本文所說的環形緩衝區足具體這種特徵的接收緩衝區,在伺服器的接收事件裡,當處理完了一次從緩衝區裡取走所有完整邏輯包的操作後,可能會在緩衝區裡遺留下來新的不完整資料包。使用了環形緩衝區後,就可以不將資料重新複製到緩衝區首郎以等待後續資料的拼裝,可以根據記錄下的佇列首部和佇列尾部指標進行下一次的拼包操作。

  環形緩衝區在IOCP的處理中,甚至在其他需要高效率處理資料收發的網路模型的接收事件處理中,是一種被廣泛採用的優化方案。

  5、實驗結果

  為了證明論文中系統優化的方法能獲得預期的效能優勢,對記憶體池和系統整體效能進行了實驗測試。測試硬體是:CPU:AMD Turion 64,記憶體:1 024 MB,網路:100 MB區域網,作業系統:Windows XP Professional SP2。

測試1:物件池效能測試

物件池效能測試結果

  由表1可以看出,由於使用物件池來分配小物件的記憶體,速度提高了52.48%,使得記憶體分配獲得了顯著的效率提升。速度提高的原閃可以歸結為以下幾點:

  (1)除了偶爾的記憶體申請和銷燬會導致從程序堆中分配和銷燬記憶體塊外,絕大多數的記憶體申請和銷燬都由物件池在已經申請到的記憶體塊中進行,而沒有直接與程序打交道,而直接與程序打交道足很耗時的操作。

  (2)這足在單執行緒環境的物件池,在多執行緒環境下,由於加鎖,因此速度提高的會少些。

  測試2:系統壓力測試

  根據上述設計,採用Visual Studio 2005開發實現的測試伺服器在壓力測試中取得很好的結果。在3 000個模擬客戶端的長時間不問斷連續資訊傳輸過程中,伺服器處理吞吐能力始終保持在1200條/s左右,並且所在伺服器作業系統狀況良好,系統資源消耗正常,佔用率穩定。

  6、結束語

  根據高效能的、可擴充套件性的伺服器實際應用需求,本文提出了基於三層結構的底層網路通訊模組設計方案,並採用多種系統優化技術來實現該模組在實際應用中的高效能和高效率。其中,執行緒池和物件池優化技術不僅在伺服器端開發上有很好的應用,也可以用於其他對效能要求較高的應用程式中。經過嚴格的效能測試,結果表明該模組在實際應用中,有非常好的表現,這也達到了筆者設計的初衷。

分類: 網遊伺服器 210人閱讀 評論(0) 收藏 舉報 伺服器網路遊戲框架server網路設計模式

隨著計算機網路的發展,特別是因特網的出現,數字娛樂和網路遊戲產業得到了蓬勃的發展,異軍突起的網路遊戲成為中國網路產業中的先鋒。而由於資訊科技的進步,計算機、手機以及不同的傳媒終端等不斷湧現,如何將這些眾多不同的終端通過網際網路進行互聯互動成為一大技術難點,而這些難點的突破取決於網路遊戲伺服器的開發。目前在各種伺服器通訊軟體的設計和開發中,已經廣泛地使用到軟體設計模式。尤其在大型的伺服器通訊軟體開發中常會採用模組化設計。當一個龐大的伺服器系統執行任務時,往往要通過其中不同的模組進行協作完成,在這種情況下各機間的資料通訊會變得異常繁瑣和複雜。另外不同作業系統通訊機制的不同也會影響通訊軟體的開發和效率,網路程式設計人員通常從底層進行開發,這大大增加了軟體開發的難度和週期。針對以上存在的問題,ACE自適應通訊構架給出了良好的解決方案,ACE能夠跨越多種作業系統平臺,可進行通用的網路程式設計任務,並結合軟體設計模式,避免了通訊軟體根據不同環境需要重新開發的弱點,減少了軟體開發的週期和資金,簡化和強化了系統的設計實現。

     1 ACE框架

      ACE自適應通訊環境(ADAPTIVE CommunicationEnvironment)是可以自由使用、開放原始碼的面向物件(OO)框架(Framework),在其中實現了許多用於併發通訊軟體的核心模式。ACE提供了一組豐富的可複用C++Wrapper Facade(包裝外觀)和框架元件,可跨越多種平臺完成通用的通訊軟體任務,其中包括:事件多路分離和事件處理器分派、訊號處理、服務初始化、程序間通訊、共享記憶體管理、訊息路由、分散式服務動態(重)配置、併發執行和同步等。ACE體系結構包括3個基本層次:作業系統適配層、C++包裝層、框架元件層。作業系統適配層直接駐留在用C寫成的本地OS API之上,將ACE中的其他層與OS API相關聯的平臺專有特性遮蔽開來。由於ACE操作適配層所提供的抽象,極大地增強了ACE的可移植性和可維護性。C++包裝層通過提供型別安全的C++介面簡化通訊應用程式的開發,各種應用可以有選擇地繼承、聚合或例項化其中的元件來使用這些包裝。ACE還包括一個高階的網路程式設計框架,整合並增強較低層次的C++包裝層,該框架支援併發分散式服務動態配置。

      大多數的網路化應用可以通過ACE這樣的可移植中介軟體進行開發,因為ACE封裝並加強了本地作業系統機制,通過其元件可以移除底層作業系統API的繁瑣和易錯性建立可重用的網路程式。

      2 網路遊戲伺服器系統框架的設計

      在設計網路伺服器的過程中,如何保證伺服器的安全和最大限度地支援更多的客戶端連線是擺在開發者面前一個重要的問題。為了解決這2大問題,根據經驗,在開發遊戲伺服器的過程中,採用如下的伺服器架構可有效解決上述2個問題。

      2.1 支援Gate的遊戲伺服器架構

      在該架構下,Client和Gate Server相連,而不是直接和Game Server相連。Gate Server主要負責轉發客戶端和Game Server之間的資料包,Game Server負責處理遊戲的所有邏輯。如圖1所示。

ic72新聞中心

      採用該架構,有如下幾個優點:客戶端通過Gate Setver和遊戲伺服器Game Servet相連,Game Server IP對外不可見,這樣Game Server更安全、更不易受攻擊。一個Game Server同樣也可以對應若干個Gate Server,當某個Gate Server受攻擊或停機後,其他的Gate Server仍然照常執行,和其他Gate Server相連的客戶端仍然可正常進行遊戲;可支援更多的客戶端連線。Gate Server把眾多的客戶端連線分散到多個Game Server中去,而不是獨自來承擔,從而可支援更多的客戶端連線;Gate Server可分擔一部分安全管理工作,減輕Game Server的壓力。例如若某個客戶端在一段時間內不發資料包,則把該客戶端踢下線的工作可由Gate Server來完成。