【问题标题】:Scalable UDP server可扩展的 UDP 服务器
【发布时间】:2011-02-12 01:07:09
【问题描述】:

我需要创建一个服务器来监听和响应 UDP 数据包, 它将监听 10 个端口。

数据包非常小,不超过 20 个字节。 每个数据包都会修改或搜索到一个巨大的哈希表。

但它必须每秒处理 15k 个数据包。

我可以使用 c、c++ 或 qt 进行开发。

有什么特殊的准则需要满足这个要求吗?使用的基本设计是什么?需要线程吗?

【问题讨论】:

  • 15k/分钟是 250/秒。这应该是一个几乎微不足道的工作量(假设您将在现代桌面/服务器平台上工作),所以我不会担心。
  • Oli,对不起,我打错了,是 15k/秒。
  • 好吧,这听起来不那么琐碎!
  • 有什么理由使用 10 个不同的端口?只是想知道..
  • 您的目标是哪些平台?

标签: c++ performance networking udp


【解决方案1】:

对于这种性能,我会考虑使用基于select() 的“换向器循环”:

  • 打开监听 UDP 套接字
  • 调用select() 以确定具有可读数据的套接字
  • 对于每个可读套接字,读取字节(非阻塞读取)
  • 当您收到完整的数据包时,处理(并根据需要写入)

您可以构建一个简单的表,在select() 结果上进行分派。

这种方法将为您提供最佳性能,因为它尽可能接近操作系统调用,并且开销最小。它作为单个进程运行,最大限度地减少上下文切换并获得最佳缓存位置。

接下来,如果您发现事情受 CPU 限制,请考虑将多个 CPU 内核与多个线程或进程一起使用的方法。例如,您能否让每个处理器处理 10 个套接字中的 N 个,并将哈希表放在共享内存中。

最后,我会小心使用大量线程(例如,大型工作线程池等)。在极端性能水平下,线程开销可能会变得很大。

【讨论】:

  • 好建议。从待处理数据包队列中提供数据的线程池可能是要走的路。
  • 实际上 poll() 和特定平台的系统调用,如 epoll() 和 kqueue 比 select() 高效得多。
  • @ldx:不一定——这取决于。在许多系统上 poll() 和 select() 非常相似(有时一个被实现为另一个薄层)。当您拥有大量文件描述符时,epoll() 和 kqueue 会表现出色。在这里,他只有 10 个具有非常高的数据包率:它们大部分时间都是可读的。
  • UDP 服务器不太可能需要 poll / epoll 等的可伸缩性,因为它只需要一个套接字。原则上,它可以使用阻塞读取来实现。
【解决方案2】:

Boost.Asio 是可扩展 UDP 服务器的完美选择。使用它比直接编程到socket APIs 有几个优点:

  1. 该库已经成熟且经过验证,实现您自己的selectpoll,epoll reactor 很可能会遇到错误。
  2. 使用int 表示所有套接字类型缺乏类型安全性,asio 使用boost::asio::ip::tcp::socketboost::asio::ip::udp::socket 等类型。
  3. 它通过促进异步设计模式而不是每个连接一个线程来扩展到thousands of concurrent connections
  4. 添加multithreading support 以增强可扩展性很简单。

【讨论】:

  • Boost.Asio 与直接 Api 调用相比有何优势?