2008-11-26 165 views
32

我有一個.NET 2.0服務器似乎正在運行到縮放問題,可能是由於套接字處理代碼設計不佳,我正在尋找關於如何我的指導可能會重新設計它以提高性能。高性能C#服務器套接字的技巧/技巧

使用場景: 50 - 150個客戶端,每個客戶端的小型消息(每個10字節)的高速率(高達100秒/秒)。客戶端連接是長期的 - 通常是幾個小時。 (服務器是交易系統的一部分,客戶端消息被聚合成組以通過更少數量的「出站」套接字連接發送給交換機,並且確認消息被髮送回客戶機,因爲每個組由交換機處理)操作系統是Windows Server 2003,硬件是2 x 4核心X5355。

當前客戶端套接字設計: A TcpListener產生一個線程來讀取客戶端連接時的每個客戶端套接字。線程在Socket.Receive上阻塞,解析傳入消息並將它們插入一組隊列中供核心服務器邏輯處理。確認消息通過客戶端套接字使用與交換端交談的線程調用異步Socket.BeginSend發回。

發現的問題:隨着客戶數量的增長(目前60-70),我們已經開始看到的間歇延遲長達數百毫秒的同時發送和從客戶端接收數據/。 (我們記錄每個確認消息的時間戳,並且我們可以看到在同一組中的一組ack通常以幾毫秒總計出現的時間戳序列中的偶爾長時間間隔。)

總體系統CPU使用率較低( < 10%),有足夠的可用RAM,核心邏輯和出站(交換)方面表現良好,所以問題似乎與面向客戶端的套接字代碼隔離。服務器和客戶端之間有充足的網絡帶寬(千兆局域網),並且排除了網絡或硬件層的問題。

任何建議或指向有用的資源將不勝感激。如果任何人有任何診斷或調試技巧來弄清楚到底發生了什麼問題,那麼這些也會很棒。

注意:我有MSDN雜誌文章Winsock: Get Closer to the Wire with High-Performance Sockets in .NET,我已經瀏覽了Kodart的「XF.Server」組件 - 它看起來很簡單。

回答

18

很多事情都與系統上運行的許多線程以及內核給每個線程分時間片有關。設計很簡單,但不能很好地擴展。

您可能應該看看使用Socket.BeginReceive,它將在.net線程池上執行(您可以指定它使用的線程數),然後從異步回調(可以運行在任何.NET線程中)。這應該會給你更高的性能。

+0

同意了,雖然我想補充一點,即使你「排除」網絡問題,我會考慮更換各種部件(特別是服務器網卡),並確保您擁有所有最新的固件和驅動程序。 – 2008-11-26 05:26:10

-1

我沒有一個答案,但獲得更多的信息我會建議灑水時間和代碼的平均代碼和最大時間用於懷疑操作,如添加到隊列或打開套接字。

至少你會知道該看什麼和從哪裏開始。

8

每個客戶端的線程看起來過於龐大,特別是考慮到整體CPU使用率較低。通常情況下,您需要一小組線程來爲所有客戶端提供服務,使用BeginReceive等待異步工作 - 然後將處理簡單地發送給其中一個工作人員(也許只需將工作添加到所有工作人員正在等待的同步隊列中)。

3

Socket.BeginConnectSocket.BeginAccept絕對有用。我相信他們在執行中使用ConnectExAcceptEx調用。這些調用將初始連接協商和數據傳輸封裝到一個用戶/內核轉換中。由於初始發送/接收緩衝區已經準備就緒,內核可以將其發送到遠程主機或用戶空間。

它們還有一個監聽器/連接器隊列就緒,這可能會通過避免用戶空間接受/接收連接並將其切斷(以及所有用戶/內核切換)所涉及的延遲而提供一點提升。

要將BeginConnect與緩衝區一起使用,看起來您必須在連接之前將初始數據寫入套接字。

6

我沒有任何延伸一個C#的傢伙,但對於高性能雙路服務器的可擴展性最強的解決方案是使用I/O Completion Ports與一些適合的CPU(一個或多個)活動線程的過程S上運行,而不是使用單線程連接模型。

在你的情況下,對於一個8核心的機器,你需要16個總線程並行運行8個。 (其他8個基本保留)

+6

CLR已經爲套接字使用了I/O完成端口。所以,你在.NET上默認獲得這個好處。 – feroze 2009-11-29 15:53:48

+0

WCF還將使用IO完成端口來回答您的每個服務呼叫。但是,讓輕量級IO端口專門爲此任務設計是一個很好的觀點。 – Spence 2010-08-26 11:15:56

4

正如其他人所建議的,實現這個將是使面臨的所有代碼異步客戶端的最佳方式。使用TcpServer()上的BeginAccept(),以便不必手動生成一個線程。然後使用從接受的TcpClient獲得的底層網絡流上的BeginRead()/ BeginWrite()。

但是,有一件事我不明白在這裏。你說這些是長期存在的連接,還有很多客戶。假設系統已經達到穩定狀態,你有最大的客戶端(比如70)連接。您有70個線程正在偵聽客戶端數據包。然後,系統應該仍然有響應。除非你的應用程序有內存/句柄泄漏,並且你的資源不足以使你的服務器進行分頁。我會在調用Accept()的地方放一個計時器,在那裏啓動一個客戶端線程並查看需要多少時間。另外,我將啓動taskmanager和PerfMon,並監視應用程序的「非分頁池」,「虛擬內存」,「處理計數」並查看應用程序是否處於資源緊縮狀態。

雖然Async是正確的路要走,但我不確定它是否能真正解決潛在的問題。我會按照我的建議監視應用程序,並確保沒有內存泄漏和處理的內在問題。在這方面,上面的「BigBlackMan」是正確的 - 你需要更多的儀器才能繼續。不知道他爲什麼被低估。

3

隨機間歇~250毫秒延遲可能是由TCP使用的Nagle算法造成的。嘗試禁用,看看會發生什麼。

1

我想消除的一件事是它不像垃圾收集器運行那麼簡單。如果你所有的消息都在堆上,你每秒產生10000個對象。

Garbage Collection every 100 seconds

唯一的解決辦法是讀保留郵件離開堆。

0

我在7年或8年前有同樣的問題,100ms到1秒的暫停,問題是垃圾收集..從4演出使用約400兆,但有很多的對象。

我結束了在下保存的郵件++但是你可以使用ASP.NET緩存(這曾經使用COM和感動他們堆出來的)