2008-11-26 106 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和感动他们堆出来的)