问题由来
网络服务在处理数以万计的客户端连接时,往往出现效率低下甚至完全瘫痪,这被称为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线程、单线程、多任务共享线程池以及一些更复杂的变种方案。常用的经典策略如下:
Serve one client with each thread/process, and use blocking I/O这是小程序和java常用的策略,对于交互式的长连接应用也是常见的选择(比如BBS)。这种策略很能难足高性能程序的需求,好处是实现极其简单, 容易嵌入复杂的交互逻辑。Apache、ftpd等都是这种工作模式。
Serve many clients with single thread, and use nonblocking I/O and readiness notification 这是经典模型,datapipe等程序都是如此实现的。优点在于实现较简单,方便移植,也能提供足够的性能;缺点在于无法充分利用多CPU的机器。尤其是程序本身没有复杂的业务逻辑时。
Serve many clients with each thread, and use nonblocking I/O and readiness notification 对经典模型2的简单改进,缺点是容易在多线程并发上出bug,甚至某些OS不支持多线程操作readiness notification。
Serve many clients with each thread, and use asynchronous I/O 在有AI/O支持的OS上,能提供相当高的性能。不过AI/O编程模型和经典模型差别相当大,基本上很难写出一个框架同时支持AI/O和经典模型,降低了程序的可移植性。在Windows上,这基本上是唯一的可选方案。
本文主要讨论模型2的细节,也就是在模型2下应用软件如何处理Socket I/O。