linux - 带有 epoll 的 TCP 服务器的线程和缩放模型

标签 linux sockets tcp epoll

我读过 C10K doc 以及许多关于扩展套接字服务器的相关论文。所有道路都指向以下内容:

  1. 避免“每个连接一个线程”的经典错误。

  2. 比 select 更喜欢 epoll。

  3. 同样,unix 中遗留的异步 io 机制可能难以使用。

我的简单 TCP 服务器仅在专用端口上的监听套接字上监听客户端连接。收到新连接后,解析请求,并发回响应。然后优雅地关闭套接字。

我想我已经很好地掌握了如何使用 epoll 在单个线程上扩展它。只有一个循环为监听套接字和现有客户端连接调用 epoll_wait。返回后,代码将处理新创建的新客户端连接以及管理现有连接的状态,具体取决于刚刚收到信号的套接字。也许还有一些逻辑来管理连接超时、套接字的优雅关闭以及每个连接的有效资源分配。看起来很简单。

但如果我想扩展它以利用多线程和多 cpu 内核怎么办?我想到的核心思想是:

一个专用线程用于监听 TCP 监听套接字上的传入连接。然后一组 N 个线程(或线程池)来处理所有事件的并发客户端连接。然后发明一些线程安全的方式,监听线程将新连接(套接字)“分派(dispatch)”到可用的工作线程之一。 (Windows 中的 IOCP)。工作线程将在它正在处理的所有连接上使用 epoll 循环来执行单线程方法将执行的操作。

我走在正确的轨道上吗?或者是否有一个标准的设计模式来使用 epoll 多线程做一个 TCP 服务器?

关于监听线程如何将新连接分派(dispatch)到线程池的建议?

最佳答案

  1. 首先,请注意它是 C*10K*。如果您少于 100(在典型系统上),请不要担心。即便如此,它也取决于您的套接字在做什么。
  2. 是的,但请记住,epoll 操作需要系统调用,它们的成本可能比您自己管理几个 fd_set 的成本高,也可能不会高. poll 也是如此。在低计数下,每次迭代都在用户空间中进行处理的成本更低。
  3. 当您不局限于可以根据需要处理的几个套接字时,异步 IO 会非常痛苦。大多数人通过使用事件循环来应对,但这会分散并反转您的程序流程。为此,它通常还需要使用大型、笨重的框架,因为可靠且快速的事件循环不容易正确实现。

第一个问题是,你需要这个吗?如果您通过生成线程来处理每个传入请求来轻松应对现有流量,那么请继续这样做。代码会因此变得更简单,并且您的所有库都会很好地发挥作用。

正如我上面提到的,处理并发请求可能很复杂。如果您想在单个循环中执行此操作,则还需要在生成响应时保证 CPU 饥饿。

如果您的响应生成成本很高,您提出的调度模型是典型的第一步解决方案。您可以 fork 或使用线程。在选择池机制时不应考虑 fork 或生成线程的成本:相反,您应该使用这种机制来限制或排序系统上的负载。

将套接字批处理到多个 epoll 循环上是过度的。如果您如此绝望,请使用多个进程。请注意,可以在来自多个线程和进程的套接字上接受

关于linux - 带有 epoll 的 TCP 服务器的线程和缩放模型,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8307618/

相关文章:

Linux TCP/IP 套接字编程

linux - 如何编写 docker 脚本以便 sudo 在 Docker 用户中运行?

java - Socket.accept() 抛出空指针异常

javascript - VB.NET TCP 客户端与 NODE-RED TCP 监听器进行双向通信

linux - 如何在另一个文件夹中执行命令并返回到原始目录?

php - 如何使用 PHPseclib 对所有磁盘大小(每个分区的)求和

c# - 从 TCP 服务器向客户端发送多条消息(C sharp 到 Android)

c# - 接收传入流时要使用的Byte []大小是多少

Java TCP套接字接受停止运行代码

python - Pyshark packet.tcp.analysis_ack_rtt 不一致可用