编写连接数巨大的高负载服务器程序时,经典的多线程模式和select 模式都不再适用。应当抛弃它们,采用epoll/kqueue/dev_poll 来捕获I/O 事件。最后简要介绍了AIO
网络服务在处理数以万计的客户端连接时,往往出现效率低下甚至完全瘫痪,这被称为C10K 问题。随着互联网的迅速发展,越来越多的网络服务开始面临C10K 问题,作为大型网站的开发人员有必要对C10K 问题有一定的了解。本文的主要参考文献是 http://www.kegel.com/c10k.htmls。C10K 问题的最大特点是:设计不够良好的程序,其性能和连接数及机器性能的关系往往是非线性的。举个例子:如果没有考虑过C10K 问题,一个经典的基于select的程序能在旧服务器上很好处理1000 并发的吞吐量,它在2 倍性能新服务器上往往处理不了并发2000 的吞吐量。这是因为在策略不当时,大量操作的消耗和当前连接数n 成线性相关。会导致单个任务的资源消耗和当前连接数的关系会是O(n)。而服务程序需要同时对数以万计的socket 进行I/O 处理,积累下来的资源消耗会相当可观,这显然会导致系统吞吐量不能和机器性能匹配。为解决这个问题,必须改变对连接提供服务的策略。
主要有两方面的策略:
1.应用软件以何种方式和操作系统合作,获取I/O 事件并调度多个socket 上的I/O 操作;
2. 应用软件以何种方式处理任务和线程/进程的关系。
前者主要有阻塞I/O、非阻塞I/O、异步I/O 这3 种方案,后者主要有每任务1 进程、每任务1 线程、单线程、多任务共享线程池以及一些更复杂的变种方案。常用的经典策略如下: 1. Serve one client with each thread/process, and use blocking I/O这是小程序和java 常用的策略,对于交互式的长连接应用也是常见的选择(比如 BBS)。这种策略很能难足高性能程序的需求,好处是实现极其简单,容易嵌入复杂的交互逻辑。Apache、ftpd 等都是这种工作模式。 2. Serve many clients with single thread, and use nonblocking I/O and readiness notification 这是经典模型,datapipe 等程序都是如此实现的。优点在于实现较简单,方便
移植,也能提供足够的性能;缺点在于无法充分利用多CPU 的机器。尤其是程 序本身没有复杂的业务逻辑时。 3. Serve many clients with each thread, and use nonblocking I/O and readiness notification 对经典模型2 的简单改进,缺点是容易在多线程并发上出bug,甚至某些OS 不支持多线程操作readiness notification。 4. Serve many clients with each thread, and use asynchronous I/O 在有AI/O 支持的OS 上,能提供相当高的性能。不过AI/O 编程模型和经典模 型差别相当大,基本上很难写出一个框架同时支持AI/O 和经典模型,降低了程 序的可移植性。在Windows 上,这基本上是唯一的可选方案。 本文主要讨论模型2 的细节,也就是在模型2 下应用软件如何处理Socket I/O。