我在 Linux 上编写了一个 UDP C++ 服务器应用程序,现在正在对其进行负载测试以查看它可以处理多少个客户端。我发现它在大约 150 个并发客户端以每秒 2-4 个的速率发送数据包时达到峰值。
之后添加的客户端将导致其他一些客户端的数据包被丢弃。
服务器本身没有压力,使用不到 10% 的 CPU 和内存。网络也完全没有压力,大约 15K 字节/秒。数据包以大约 200 个数据包/秒的速度到达服务器(它使用一个 UDP 套接字进行读取和写入)。在此负载级别,服务器线程本身仍会短时间休眠。
问题:
关于这里的瓶颈是什么有什么想法吗?CPU、网络和服务器代码本身似乎都没有压力。操作系统是否无法处理此数量的 UDP 数据包?
硬件功耗非常低 - 相当于 1.5 MHz 的单核 Pentium。NIC 为 100M 位/秒。我正在运行 Ubuntu 11.1。
本文可能与此相关:Windows Server 2008 上 UDP 性能的上限
更新:服务器设置一个 UDP 套接字,然后创建 3 个线程和 2 个队列。套接字读取的第一个线程块如下所示:
while (1)
{
recvfrom(this->socket, readBuf, BUFSIZE, 0, (sockaddr *)&address, &addressLen);
pushBack(this->inputQueue, message);
}
第二个线程在 inputQueue 上休眠。它在发出条件信号并处理消息时唤醒。它将处理后的消息发送到 outputQueue:
while (1)
{
sleepOnQ(this->inputQueue);
popFront(this->inputQueue);
processMessage();
pushBack(this->outputQueue, message);
}
第三个线程在 outputQueue 上休眠并将消息从 UDP 套接字发送到目的地。请注意,它与用于读取的套接字相同。
while (1)
{
sleepOnQ(this->outputQueue);
popFront(this->outputQueue);
processMessage();
sendto(this->socket, message, ... );
}
每个客户端和消息的处理量很小。正如我提到的,当服务器每秒处理 200 条消息时,它使用了大约 10% 的非常弱的 CPU。
以下是系统上的一些内核参数:
net.core.wmem_max = 114688
net.core.rmem_max = 114688
net.core.wmem_default = 114688
net.core.rmem_default = 114688
有关数据同步的更多信息
到目前为止的答案让我认为发生了两件事:
- 操作系统读取缓冲区已满。但是在低 CPU 的情况下,这不应该发生
- 但是如果线程正在等待其他事件并且因此没有足够快地读取套接字,则可能会发生#1。
日志记录可能是一个问题,我会尝试将其关闭并报告结果。然而,可能更重要的是线程之间的队列争用。由于 CPU 较低,线程可能会花费大量时间等待访问队列。
在这个服务器的第一次迭代中,我试图在锁定数据方面变得棘手。服务器非常快,但在达到 800 个数据包/秒时崩溃了。当前版本锁定整个队列。也许我需要一种更好的方法来同步线程。
已回答问题
我在这里得到的信息非常有帮助。问题是测试客户端的一个愚蠢的错误,但进行调查帮助我消除了这里建议的原因。
仅供参考,这是我的结果。一旦我解决了客户端的问题,服务器每秒接受大约 800 个数据包,CPU 利用率为 70%。我已将操作系统读/写缓冲区从 128K 增加到 12MB。我没有测试读取缓冲区是否已填满。我怀疑操作系统读取缓冲区是一个问题,因为在最高速度下,服务器读取线程仍然在每 10 次或 20 次读取时在短时间内阻塞读取。
800 个数据包/秒仍然太慢,所以我从服务器中删除了日志记录。这产生了巨大的变化。服务器能够以 70% 的 CPU 利用率从 1400 多个客户端接收 2900 条消息/秒。
我还对读取线程是否正在等待锁定进行了一些测试。即使在最高速度下,我也发现它永远不必等待超过 1 毫秒,因此它不是 2900 条消息/秒的因素。也许它将在更快的 CPU 上。
此时服务器受 CPU 限制,要找到下一个瓶颈,我需要使用更强大的 CPU。谢谢你的帮助!