2

我在 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

有关数据同步的更多信息

到目前为止的答案让我认为发生了两件事:

  1. 操作系统读取缓冲区已满。但是在低 CPU 的情况下,这不应该发生
  2. 但是如果线程正在等待其他事件并且因此没有足够快地读取套接字,则可能会发生#1。

日志记录可能是一个问题,我会尝试将其关闭并报告结果。然而,可能更重要的是线程之间的队列争用。由于 CPU 较低,线程可能会花费大量时间等待访问队列。

在这个服务器的第一次迭代中,我试图在锁定数据方面变得棘手。服务器非常快,但在达到 800 个数据包/秒时崩溃了。当前版本锁定整个队列。也许我需要一种更好的方法来同步线程。

已回答问题

我在这里得到的信息非常有帮助。问题是测试客户端的一个愚蠢的错误,但进行调查帮助我消除了这里建议的原因。

仅供参考,这是我的结果。一旦我解决了客户端的问题,服务器每秒接受大约 800 个数据包,CPU 利用率为 70%。我已将操作系统读/写缓冲区从 128K 增加到 12MB。我没有测试读取缓冲区是否已填满。我怀疑操作系统读取缓冲区是一个问题,因为在最高速度下,服务器读取线程仍然在每 10 次或 20 次读取时在短时间内阻塞读取。

800 个数据包/秒仍然太慢,所以我从服务器中删除了日志记录。这产生了巨大的变化。服务器能够以 70% 的 CPU 利用率从 1400 多个客户端接收 2900 条消息/秒。

我还对读取线程是否正在等待锁定进行了一些测试。即使在最高速度下,我也发现它永远不必等待超过 1 毫秒,因此它不是 2900 条消息/秒的因素。也许它将在更快的 CPU 上。

此时服务器受 CPU 限制,要找到下一个瓶颈,我需要使用更强大的 CPU。谢谢你的帮助!

4

2 回答 2

1

如果传入的 UDP 数据报不适合 UDP 输入缓冲区(通常缓冲区已满),则内核将丢弃它。

如果在数据包速率仅为 200/s 且 CPU 负载较低时缓冲区已满,那么您的程序会浪费时间等待新数据包之外的其他内容(睡眠结束、某些资源等)。

仔细检查您的代码。并尝试摆脱所有sleep,nanosleep和类似的睡眠功能。

如果您将大量(调试)输出打印到串行控制台,它可能会开始阻塞您的程序,因为串行端口不是那么快。尝试消除这种瓶颈。

于 2012-12-06T09:41:47.847 回答
1

丢失的最可能原因是 UDP 套接字的传入数据包缓冲区在您的第一个线程可以清空它之前填满;在缓冲区已满时接收到的任何传入 UDP 数据包都将被丢弃。

第一个线程无法以足够快的速度清空缓冲区以防止其被填满的最可能原因是其他东西将其与 CPU 隔离太久......核心CPU,很可能就是这种情况。您可能想尝试将您的第二个和第三个线程设置为较低的优先级(这样当出现争用时第一个线程将在 CPU 上获得第一个 dib)并查看是否有帮助。如果这还不够好,您可以将您的进程设置为 SCHED_RR“实时”优先级,以确保操作系统中运行的任何其他进程不会让您的第一个线程远离 CPU。(当然,您仍然可以以较低的优先级运行其他线程,因为它们执行的确切时间并不重要)。

于 2012-12-06T07:33:36.867 回答