【发布时间】:2014-03-27 14:37:55
【问题描述】:
我有一个基于TCPListener 的 ECHO 服务器应用程序。它接受客户端、读取数据并返回相同的数据。我使用框架提供的 XXXAsync 方法使用 async/await 方法开发了它。
我已经设置了性能计数器来测量有多少消息和字节进出,以及有多少连接的套接字。
我创建了一个测试应用程序,它启动 1400 个异步TCPClient,并每 100-500ms 发送一条 1Kb 的消息。客户端在开始时有 10-1000 毫秒之间的随机等待开始,因此他们不会尝试同时连接所有客户端。我工作得很好,我可以在 PerfMonitor 中看到 1400 已连接,以良好的速度发送消息。我从另一台计算机运行客户端应用程序。服务器的 CPU 和内存使用量很少,它是 Intel Core i7 和 8Gb RAM。客户端似乎更忙,它是具有 4Gb RAM 的 i5,但仍然没有 25%。
问题是如果我启动另一个客户端应用程序。客户端中的连接开始失败。我没有看到每秒消息的大幅增加(或多或少增加了 20%),但我看到连接的客户端数量大约是 1900-2100,而不是预期的 2800。性能略有下降,图表显示每秒最大和最小消息之间的差异比以前更大。
不过,CPU 使用率甚至不到 40%,内存使用率仍然很少。我试图增加客户端和服务器中的线程数或池线程数:
ThreadPool.SetMaxThreads(5000, 5000);
ThreadPool.SetMinThreads(2000, 2000);
在服务器中,循环接受连接:
while(true)
{
var client = await _server.AcceptTcpClientAsync();
HandleClientAsync(client);
}
HandleClientAsync 函数返回一个Task,但正如您所见,循环不等待处理,只是继续接受另一个客户端。那个处理函数是这样的:
public async Task HandleClientAsync(TcpClient client)
{
while(ws.Connected && !_cancellation.IsCancellationRequested)
{
var msg = await ReadMessageAsync(client);
await WriteMessageAsync(client, msg);
}
}
这两个函数只是异步读写流。
我已经看到我可以启动 TCPListener 指示 backlog 金额,但默认值是多少?
为什么应用程序在达到最大 CPU 之前无法扩展?
找出实际问题的方法和工具是什么?
更新
我尝试了Task.Yield 和Task.Run 方法,但它们没有帮助。
服务器和客户端在同一台计算机上本地运行时也会发生这种情况。每秒增加客户端或消息的数量,实际上会降低服务吞吐量。 600 个客户端每 100 毫秒发送一条消息,比 1000 个客户端每 100 毫秒发送一条消息产生更多的吞吐量。
连接超过 2000 个客户端时,我在客户端上看到的异常是两个。大约 1500 我在开始时看到了异常,但客户端最终连接了。超过 1500 我看到很多连接/断开连接:
“一个现有的连接被远程主机强行关闭” (System.Net.Sockets.SocketException) 一个 System.Net.Sockets.SocketException 被捕获:“现有连接 被远程主机强行关闭”
“无法将数据写入传输连接:一个现有的 连接被远程主机强行关闭。” (System.IO.IOException) 抛出 System.IO.IOException:“无法 将数据写入传输连接:现有连接是 被远程主机强行关闭。”
更新 2
我已经设置了一个非常 simple project with server and client using async/await 并且它可以按预期扩展。
我遇到可扩展性问题的项目是this WebSocket server,即使它使用相同的方法,显然也有一些东西引起了争用。有一个console application hosting the component 和一个generate load 的控制台应用程序(尽管它至少需要Windows 8)。
请注意,我不是要直接解决问题的答案,而是要找出导致争用的原因的技术或方法。
【问题讨论】:
-
好吧,请务必查清楚!在他们那一刻,我们只知道“某处出现错误”。
-
除了确切的错误信息,请告诉我们您是否(以及如何)明确使用线程池,
Task.Run、Task.Factory.StartNew等。 -
@vtortola,我的意思是不使用
Task.Run。这就是我的意思:stackoverflow.com/a/21018042/1768303。也许你应该展示你的HandleClientAsync的样子。 -
@vtortola:您绝对确定需要使用TCP/IP ?因为
HandleClientAsync有几个问题:它使用Connected,它在没有同时定期写入的情况下读取,在没有同时连续读取的情况下写入。 TCP/IP 与编写汇编语言没有什么不同。在克林贡。有什么方法可以改用 WebAPI 和/或 SignalR? -
@vtortola:我强烈建议你选择另一个项目来学习
async/await。学习 TCP/IP 本身就是一项艰巨的任务。不,阻塞不是我的意思;使用读/写循环,您将面临半开问题。
标签: .net multithreading asynchronous async-await tcplistener