Multithreading TCP/IP—使用每客户端线程方法解决C10K问题

Multithreading TCP/IP—使用每客户端线程方法解决C10K问题,multithreading,concurrency,tcp,c10k,Multithreading,Concurrency,Tcp,C10k,在阅读了这篇著名的文章并在网络上搜索了自它被编写以来事情的发展之后,我想知道今天的标准服务器是否有可能使用每个连接一个线程来处理10000个并发连接(可能借助线程池来避免创建/终止过程) 可能影响问题解决方法的一些细节: 输入、中间处理和输出 每个连接的长度 服务器的技术规格(核心、处理器、RAM等) 将此系统与AIO、轮询、绿色线程等替代技术相结合 显然,我不是这方面的专家,因此任何评论或建议都将受到高度赞赏:)服务器的常用方法是:(a)每个连接的线程(通常使用线程池),或(b)使用异步I

在阅读了这篇著名的文章并在网络上搜索了自它被编写以来事情的发展之后,我想知道今天的标准服务器是否有可能使用每个连接一个线程来处理10000个并发连接(可能借助线程池来避免创建/终止过程)


可能影响问题解决方法的一些细节:

  • 输入、中间处理和输出
  • 每个连接的长度
  • 服务器的技术规格(核心、处理器、RAM等)
  • 将此系统与AIO、轮询、绿色线程等替代技术相结合


  • 显然,我不是这方面的专家,因此任何评论或建议都将受到高度赞赏:)

    服务器的常用方法是:(a)每个连接的线程(通常使用线程池),或(b)使用异步IO的单线程(通常使用epoll或kqueue)。我的想法是,这些方法中的一些元素可以而且通常应该结合使用异步IO(与epoll或kqueue一起),然后将连接请求传递给线程池进行处理。这种方法将异步IO的高效调度与线程池提供的并行性结合起来


    我编写了这样一个有趣的服务器(用C++编写),它在Linux上使用epoll,在FreeBSD和OSX上使用kqueue,并带有线程池。我只需要对它进行全面的测试,进行一些代码清理,然后将它放到github上(希望很快就可以了)。

    您可能会喜欢最近对这个主题的后续介绍:。

    当然。使用每个连接一个线程的模型,标准服务器可以处理超过10K个并发连接。我构建了这样一个应用程序,五年前,它在标准Linux服务器上运行时,每个进程的并发连接数超过50K。现在,在当前硬件上运行同一个应用程序时,并发连接超过250K应该是可能的

    只有几件事需要记住:

    • 通过使用线程池重用线程。如果不使用线程,则无需终止线程,因为资源使用应针对峰值负载进行优化
    • 堆栈大小:默认情况下,每个Linux线程为其堆栈保留8MB。对于10K线程而言,总计为80 GB。您应该将默认堆栈大小设置为64k和512k之间的某个值,这不是问题,因为大多数应用程序不需要更深的调用堆栈
    • 如果连接是短期的,请通过使用选项
      SO\u REUSEPORT
      在同一端点上创建多个套接字来优化新连接
    • 增加用户限制:
      打开文件
      (默认值1.024),
      最大用户进程数
    • 增加系统限制,例如
      /proc/sys/kernel/pid_max
      (默认值32K)、
      /proc/sys/kernel/threads max
      ,以及
      /proc/sys/vm/max_map_count
      (默认值65K)

    上述应用程序最初设计为仅处理2K并发连接。然而,随着使用量的增加,我们不必对代码进行重大更改就可以扩展到50K连接。

    也许是我有点困惑,但我认为
    epoll
    并不是完全异步的。经过一些研究,我发现有几篇文章说,
    AIO
    与线程池结合使用会更好,而
    epoll
    与单个线程结合使用会更好:……我还了解到,在实现
    AIO
    方面存在一些困难,因此,使用
    epoll
    +线程池方法可能更有利可图。我自己的想法是,可以直接使用池中的一个线程来切换每个连接,直到同时连接的数量接近系统可以支持的最大线程数量,然后,我们可以使用…epoll/kqueue/AIO系统在所有线程都繁忙时处理新的传入连接。(P.S:我很想看到你的服务器很快,特别是如果它是用C++编写的):@ Strut1: EPOLL是每个套接字异步的,AIO是每个请求异步的。也就是说,如果您可以保证每个套接字只在一个特定的线程中使用(顺便说一句,无论出于多个原因使用epoll(包括缓存位置),这都是一个好主意(tm)),那么在多线程环境中使用epoll就可以了。多么了不起的文章人!!我想我可以从中学到很多东西,谢谢:)@Str1101我也觉得它很有启发性。这让我再次想到了外核。这本质上是使用线程调度系统作为数据包调度系统:线程调度程序根据到达的数据确定下一步调用哪个
    read()
    。很明显,它是可行的,但有更多可扩展的设计。非常简单:虽然这是可能的,但它很少是一个好主意(线程切换成本很容易达到100K CPU周期,拥有太多线程会毫无理由地变得昂贵)。