The C10K problem 知识点整理

来源:互联网 发布:网络游戏洗钱 知乎 编辑:程序博客网 时间:2024/06/05 23:09

The C10K problem

我们的服务器很容易就能支持上万数目的,而如今硬件与带宽更不是问题,如何编写能支持大量并发服务的软件呢。

下面收录别人做的一个衡量fork性能的测试,各个版本的操作系统fork子进程所消耗的时间都比较多,其中linux 2.6的性能比较好。


从上面的图可以看出,假设每创建子进程消耗500微秒,那么一秒钟可以创建2000个进程,如果加上每个进程还要处理繁重的业务逻辑,那只靠多进程的方式提供高并发是不可能的,所以我们需要利用更加高效的I/O模型。

一些I/O框架:ACE(一个大型的C++的I/O框架,内部都是面向对象的接口),ASIO(Boost库中C++版本的I/O框架),libevent是个轻量级的C实现的I/O框架,Poller,rn等等等。

I/O策略——网络程序开发有几种选择方案如下

  • 是否处理并发的I/O请求,如果在单线程中处理并发I/O请求
    • 不处理,阻塞/同步处理请求;可能得话用多线程和多进程达到并发目的
    • 使用非阻塞调用,比如针对socket描述符设置非阻塞位,通过检测一些状态位,主要用在网络I/O而不是磁盘I/O上
    • 使用异步调用(如aio_write函数操作I/O)完成后通过信号或完成端口通知,可以使用在网络I/O与磁盘I/O
  • 如何控制代码为客户端提供服务
    • 一个进程服务一个客户端
    • 一个系统级线程服务所有客户端,每个客户端由下面几种提供服务
      • 用户级线程
      • 状态机
      • continuation(不知是什么)
    • 一个系统级线程服务一个客户端
    • 一个系统级线程服务一个活动的客户端(完成端口,线程池)
  • 是否使用操作系统级服务,或把代码植入内核(驱动,系统模块,VxD等)

下面五个组合是比较常用的

  1. 一个线程服务多个客户端,使用非阻塞I/O和水平触发
  2. 一个线程服务多个客户端,使用非阻塞I/O和边沿触发
  3. 一个线程服务多个客户端,使用异步I/O
  4. 一个线程服务一个客户端,使用阻塞I/O
  5. 将服务代码植入内核

下面分别看看它们的特点

1,一个线程服务多个客户端,使用非阻塞I/O和水平触发

为所有网络句柄设置非阻塞位,使用select或poll函数检测那些句柄存在数据读写,这是传统的设计选择。在这种模式下,系统告诉你一个文件描述符是否准备好,是否完成自上次告诉你以后指定的任务。(水平触发对应边沿触发,是硬件设计上的名次,后者更为实时高级,IOCP与AIO中使用到),这种触发例如select,poll,/dev/poll,kqueue等函数。

注意:通过select与poll函数检测描述符可用并不完全可靠,因为当我们真正读写的时候并不一定准备好,所以需要记录状态信息。

这种模式的瓶颈在于读写磁盘文件时(比如所需页内容不在内存),设置描述符为非阻塞没有意义,当一个服务需要磁盘I/O时,进程和客户端需要等待,单线程的优势就废掉了。

2,一个线程服务多个客户端,使用非阻塞I/O和状态码改变通知

“边沿触发”指的是,我们向系统指定一个文件描述符,当描述符从不可用变可用时,系统会通知我们。系统假定我们知道了描述符可用,就不需要记录什么可用状态码之类的信息知道我们去操作描述符使之又一次不可用。这种模式包括kqueue函数以及epoll函数等(kqueue可以指定水平触发或者边沿触发)。

3,一个线程服务多个客户端,使用异步I/O

这种方式还没有在Unix中普及,毕竟少数系统支持异步I/O,需要我们重构代码。在标准Unix中,异步I/O由the_aio_interface提供,它把一个信号和值与每一个I/O操作关联,信号和值被放入队列传递给用户进程。AIO一般使用边沿触发,在windows平台,响应的异步I/O机制是IOCP。

4,一个线程服务一个客户端,使用阻塞I/O

虽然现今的线程单独阻塞提供服务性能很低,但是随着线程技术的更新与发展,将来提供10000个客户端的并发也不是没有可能。

5,将服务代码植入内核(这部分不懂就不提了)

原创粉丝点击