.NET 客户端/服务器可扩展性和异步 I/O - 线程过多问题
我有一个非常简单的问题,可能是熟悉服务器/客户端设计和的人? .NET 的异步 I/O 范例可以快速回答...
我正在编写一个服务器端应用程序,该应用程序设计为在相对不复杂的硬件上运行(阅读:不太现代的普通办公室台式电脑),但要容纳相当多的用户(尽管有些用户可能闲置) - 因此,我担心每个客户端使用单个线程对可伸缩性的影响(由于过多的上下文切换导致机器停止运行)。当然,这需要仔细考虑何时真正应该生成线程。
我考虑过的一种解决方案是在服务器应用程序中有一个主线程(与连接侦听器线程分开),它通过定期查看打开的套接字列表并执行 select(...) 来跟踪打开连接上的活动每个。如果线程找到传入数据,它会设置异步读取(以使用消息、处理消息并返回回复(如果需要)),然后移至下一个套接字。无论服务器至少有一个客户端连接多久,都会重复此过程。
这样做时,实际运行的最大线程数仅限于实际通信的客户端数量(并且永远不会高于连接的最大用户数量);对于空闲的客户端,不存在空闲线程,它只是占用 CPU 并使机器陷入困境 - 线程与客户端关联的唯一时间是当有实际数据要接收和发送时。已处理。
在我看来,这是有道理的,尽管我想这是我第一次在 C# 中尝试这种方式。有没有人对这可能带来的任何问题有任何想法,或者对完成相同任务的更好方法提出建议?
谢谢!
I have a pretty simple question which perhaps someone familiar with Server/Client design & the Asynchronous I/O paradigm of .NET could answer quickly...
I'm writing a server-side application which is designed to run on relatively non-sophisticated hardware (read: not-so-modern, average office desktop PCs), but accommodate a reasonably large number of users (though some users may be idle) - Because of this, I am concerned about the scalability impact of having a single thread for each client (due to excess context switching grinding the machine to a halt). This of course requires careful thought as to when a thread truly should be spawned.
One solution I've considered is to have a single master thread (separate from the connection listener thread) in the Server App which tracks activity on open connections by regularly going down the list of open sockets and performing a select(...) on each. If the thread finds incoming data, it sets up an asynchronous read (to consume the message, process it and return a reply, if necessary), and then moves onto the next socket. This process repeats for however long the server has at least on client connected.
In doing so, the maximal # of threads that are actually running is limited to only the number of clients actually communicating (and never higher than the maximal number of users connected); For clients that are idle, there is no idle thread which is simply taking up the CPU and bogging down the machine - The only time a thread is associated with a client is when there is actual data to be received & processed.
In my mind, this makes sense, though I guess this is the first time I've ever attempted something of this manner in C#. Does anyone have any thoughts on any issues this might bring about, or suggestions on maybe a better way of doing the same task?
Thanks!
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
您可能需要考虑的一种方法是使用 ThreadPool为您的应用程序池提供可用线程。
这种方法与您正在考虑的方法不同,因为您的服务器可以同时处理固定数量的线程,超过该数量后收到的任何请求都将排队,直到另一个工作线程可用。
事实上,这就是 ASP.NET 用于传入 HTTP 请求的方式。
One approach you may want to consider is using the ThreadPool to have a pool available threads for your applications.
This approach would differ from what you are considering in that there would be a fixed amount of threads your server could ever handle at the same time, and any requests received after that number is exceeded would be queued until another worker thread becomes available.
This is, in fact, what ASP.NET is using for incoming HTTP requests.
您考虑过使用WCF吗?如果您担心上下文切换会占用 CPU 资源,那么最好使用调用并关闭方法。 WCF 可以极大地简化您的客户端-服务器通信,因为您甚至根本不必担心套接字编程。它还提供了多种开箱即用的通信选项,包括 HTTP、TCP、命名管道(仅限同一台计算机)和 MSMQ。
根据客户端和服务器之间的对话具体如何工作,您也许可以使用 MSMQ 和发布/订阅消息传递,这将允许您的客户端异步发送消息并获得异步回复。这将扩展到几乎任何负载。如果您有有状态对话,WCF 也支持此功能,并且如果服务器需要回调客户端,则双工通道可用于双向多消息对话。
WCF 将为您处理线程、队列和资源管理,使您能够专注于关键业务行为。至于吞吐量,我最近使用超级基本计算器服务(add、sub、mul、div 方法,基本上为零行为开销)对 WCF 进行了简单、原始的性能测试。在我工作的开发人员工作站(一台具有几GB RAM 的 Core 2 Duo)上,我们每秒实现了 30,000 次调用,同时还有四台其他开发人员机器在运行。相比之下,如果是一台更现代的机器,我的家用系统配备 Core i7 920(8 个逻辑核心)、12 GB 内存和千兆以太网,我什至无法让管道或 CPU 与其他 4 个系统同时使用。 ,并实现了每秒近10万次调用。如果我能够设法使管道和/或 cpu 饱和,我什至无法想象原始吞吐量。
我认为这有力地表明 WCF 本身的开销非常低,即使在较旧的硬件上也不应该给您带来问题。
我强烈建议研究一下。它可以为您节省大量开发和维护自定义通信基础设施的时间。
Have you considered using WCF? If you are concerned about context switching swamping the cpu, it might be better to use a call-and-close approach. WCF can greatly simplify your client-server communications, as you won't even have to worry about socket programming at all. It provides a wide variety of communication options out of the box as well, including HTTP, TCP, Named Pipes (same machine only), and MSMQ.
Depending on exactly how your conversations between client and server work, you may be able to go with MSMQ and pub/sub messaging, which would allow your clients to send messages asynchronously, and get asynchronous replies. This would scale to almost any load. If you have stateful conversations, WCF supports this as well, and if the server needs to call back to the client, duplex channels are available for dual-way, multi-message conversations.
WCF will handle threading, queuing, and resource management for you, allowing you to focus on the critical business behaviors. As for throughput, I recently did a simple, raw performance test of WCF using a super basic calculator service (add, sub, mul, div methods, basically zero behavioral overhead). On my developer workstation at work, a Core 2 Duo with a couple gigs of ram, we achieved 30,000 calls per second with four other developer machines hitting it. In contrast, a more modern machine, my home system with a Core i7 920 (8 logical cores), 12 gigs of ram, and gigabit ethernet, I wasn't even able to saturate the pipe or CPU's with 4 other systems hitting to it, and achieved nearly 100,000 calls per second. I can't even imagine the raw throughput if I could manage to saturate the pipe and/or cpu.
I think thats a strong indication that the overhead of WCF itself is ridiculously low, and shouldn't pose a problem for you, even on older hardware.
I highly recommend looking into it. It could save you a considerable chunk of time developing and maintaining a custom communications infrastructure.