五种IO模型

阻塞式IO

当应用程序调用IO操作时,进程会被阻塞,直到数据准备好并且从内核空间复制到用户空间完成后才返回。

特点:

工作流程: 应用程序发起IO请求 → 内核准备数据 → 数据从内核复制到用户空间 → 返回结果

非阻塞式IO

应用程序调用IO操作时,如果数据没有准备好,内核立即返回错误,不会阻塞进程。应用程序需要不断轮询检查数据是否准备好。

特点:

工作流程: 应用程序轮询检查 → 数据未准备好时立即返回错误 → 数据准备好后进行复制 → 返回结果

IO多路复用

由一个线程监控多个网络请求(linux系统把所有网络请求以一个文件描述符来标识),来完成数据状态询问的操作,当有数据准备就绪之后再分配对应的线程去读取数据。使用select、poll、epoll等系统调用,可以同时监控多个文件描述符,当其中任何一个准备好时就通知应用程序。

特点:

工作流程: 注册多个文件描述符到多路复用器 → 阻塞等待任一描述符就绪 → 处理就绪的描述符 → 数据复制到用户空间

信号驱动IO

原理: 应用程序向内核注册一个信号处理函数,当数据准备好时,内核发送信号通知应用程序,然后应用程序在信号处理函数中进行IO操作。

特点:

工作流程: 注册信号处理函数 → 继续执行其他任务 → 内核数据准备好后发送信号 → 信号处理函数中进行数据复制

异步IO

原理: 应用程序发起IO请求后立即返回,内核负责完成整个IO操作(包括数据准备和复制),完成后通知应用程序。

特点:

工作流程: 发起异步IO请求 → 立即返回继续执行 → 内核完成数据准备和复制 → 通过回调或事件通知应用程序

总结对比

IO模型阻塞性并发能力适用场景
阻塞式IO完全阻塞简单单线程程序
非阻塞式IO轮询阶段不阻塞中等低并发、需轮询的场景
IO多路复用监控阶段阻塞高并发、多连接场景(首选)
信号驱动IO不阻塞需异步通知的中等并发场景
异步IO完全不阻塞最高高并发、实时性要求高的场景

并发服务器模型

循环式迭代式模型

循环式迭代式模型是一种简单的服务器处理模型,服务器按顺序处理客户端请求。

一种单线程的应用程序,只能使用短连接而不能使用长连接,缺点是无法充分利用多核CPU,不适合执行时间较长的服务,即适用于短连接(这样可以处理多个客户端),如果是长连接则需要在read/write之间循环,那么只能服务一个客户端。

所以循环式服务器只能使用短连接,而不能使用长连接,否则无法处理多个客户端的请求,因为整个程序是一个单线程的应用程序。

特点:

循环式迭代式模型

并发式服务器

并发式服务器能够同时处理多个客户端请求,通过多进程或多线程实现并发处理。

适合执行时间比较长的服务。在父进程中要关闭创建连接的套接字,等待下一个客户端请求连接,所以可以并发处理多个客户端的请求,一个客户端一个进程;子进程在处理客户端的请求,子进程是长连接的,不断处理请求,即使子进程中解包,计算,打包的过程时间过长,也不会影响父进程去连接其他客户端的请求。也适用于线程,主线程每次accept回来就创建一个子线程服务,由于线程共享文件描述符,故不用关闭,最后一个线程关闭监听套接字。

特点:

并发式服务器

 

prefork服务器

Prefork服务器预先创建一定数量的子进程,每个子进程处理客户端请求。

由于处理连接的进程/线程都是预先创建好的,因此可以减小创建进程/线程的开销,能够提高响应速度。进程预先fork了n个子进程(n的数目需要提前设定),每个子进程负责和客户端的通信,每个子进程执行的都是右图的流程。

前面的创建套接字,绑定端口号,监听套接字这些步骤,每个预先创建的进程已经完成,他们分别调用accept并由内核置入睡眠状态。

这种服务器的优点是:提高了响应速度,不需要引入父进程执行fork的开销,新客户就能得到处理。

缺点在于:每次启动服务器,父进程必须预测到底需要产生多少子进程,还有一个就是如果不考虑再派生子进程,先前派生的子进程可能被客户请求占用完,以后新到的请求只能先完成三次握手,并且达到listen接口的最大并发连接数backlog,直到有子进程可用。服务器才调用accept,将这些已经完成的连接传递给accept。

特点:

prefork服务器

 

反应式服务器

反应式服务器使用事件驱动的方式处理请求,通常基于IO多路复用技术。

服务器可以并发处理多个请求。不过本质上这些请求还是在一个线程中完成的,即:单线程轮询多个客户端。也无法充分利用多核CPU,不适合执行时间比较长的服务,所以为了让客户感觉是在“并发”处理而不是“循环”处理,每个请求必须在相对较短时间内执行。当然如果这个请求不能在有限的时间内完成,我们可以将这个请求拆分开来,使用有限状态机机制来完成。其中的Reactor可以使用IO多路复用select/poll/epoll来完成。

多路:监听多个文件描述符

复用:复用同一个进程(或者说同一个线程)

特点:

反应式服务器

模型的过程解析:

  • Reactor是一个线程对象,该线程会启动事件循环,并使用select/poll/epoll来实现IO多路复用。注册一个Acceptor事件处理器到Reactor中,Acceptor事件处理器所关注的事件是accept事件,这样Reactor会监听客户端向服务器端发起的连接请求事件。

  • 客户端向服务器端发起一个连接请求,Reactor监听到了该accept事件的发生并将该accept事件派发给相应的Acceptor处理器来进行处理。Acceptor处理器通过accept方法得到与这个客户端对应的连接,然后将该连接所关注的读事件以及对应的read读事件处理器注册到Reactor中,这样Reactor就会监听该连接的read事件了。或者当需要向客户端发送数据时,就向Reactor注册该连接的写事件和其处理器。

  • 当Reactor监听到有读或者写事件发生时,将调用dispatch将相关的事件分发给对应的处理器进行处理。比如,读处理器会通过read方法读取数据,此时read操作可以直接读取到数据,而不会堵塞与等待可读的数据到来。

  • 每当处理完所有就绪的I/O事件后,Reactor线程会再次执行IO多路复用的函数阻塞等待新的事件就绪并将其分派给对应处理器进行处理。

目前的单线程Reactor模式中,不仅I/O操作在该Reactor线程上,连非I/O的业务操作也在该线程上进行处理了,这可能会大大延迟I/O请求的响应。所以应该将非I/O的业务逻辑操作从Reactor线程上分离出去,以此来加速Reactor线程对I/O请求的响应。这种的服务器的并发量比并发式服务器多,因为并发式服务器能够创建的进程或者线程数目是有限的。

反应式 + 线程池型服务器

反应式 + 线程池型服务器结合了反应式模型和线程池的优势。

与单线程Reactor模式不同的是,增加了线程池对象,并将业务逻辑的处理从Reactor线程中移出转交给工作的线程池来执行。这样能够提高Reactor线程的I/O响应,不至于因为一些耗时的业务逻辑而延迟对后面I/O请求的处理。

特点:

反应式_线程池型服务器

使用线程池带来的好处有如下几点:

在这个模型之下,线程池中的线程处理完业务逻辑,需要与Reactor线程进行通信,把结果交给Reactor,再由它发送给客户端。

(必然要通过线程间通信,让Reactor线程与线程池中的工作线程交换数据)

多反应式服务器

多反应式服务器运行多个反应式事件循环,通常每个CPU核心对应一个事件循环。上面的模型中,Reactor代表的是一个进程(一个线程),既负责监听,又负责IO,如果在执行监听或者执行IO时是采用的阻塞式,也会导致其他的业务等待

进一步改进:

一个主线程,多个工作线程,主线程Reactor负责接收客户端连接,每个线程有各自的Reactor负责执行任务队列中的任务。

特点:

多反应式服务器

多反应式+ 线程池模型

多反应式 + 线程池模型是最复杂但也是性能最优的服务器架构。

多个Reactor的模式,mainReactor与subReactor都是一个线程,因为多进程之间无法共享计算线程池。这种模型能够适用IO频繁且计算密集的服务。

特点:

image-20250318175109135

Reactor模型

测试客户端代码

ReactorV1

类图

Reactor_v1

完整代码

说明

这个版本实现了基本的TCP服务器功能。服务器接收客户端连接,读取客户端发送的消息,将消息中的字母转换为大写后返回给客户端。

项目结构

组件说明

1. Socket类

2. InetAddress类

3. SocketIO类

4. Acceptor类

5. TcpConnection类

程序流程

  1. 服务器启动: 创建Acceptor对象,绑定到127.0.0.1:12345

  2. 准备监听: 调用ready()方法完成socket配置和监听

  3. 接受连接: 等待并接受客户端连接,创建TcpConnection对象

  4. 消息处理: 循环接收客户端消息,将字母转为大写后返回

  5. 持续服务: 在无限循环中持续为当前连接提供服务

局限性

  1. 单连接处理: 一次只能处理一个客户端连接

  2. 阻塞IO: 使用传统的阻塞IO模型

  3. 无并发: 不支持多客户端同时连接

ReactorV2

类图

Reactor_v2

完整代码

说明

Reactor_v2 是基于 Reactor 模式的网络服务器实现的第二个版本,相比 v1 版本,引入了事件循环机制,使用 epoll 实现了高效的 I/O 多路复用,能够同时处理多个客户端连接。

核心特性

1. 事件驱动架构

2. 回调机制

3. 连接管理

组件

EventLoop(事件循环)

Acceptor(连接接受器)

TcpConnection(TCP连接)

Socket、SocketIO、InetAddress

工作流程

  1. 初始化阶段

  2. 设置回调函数

  3. 启动事件循环

  4. 事件处理

    • 监听套接字有事件 → 处理新连接

    • 客户端套接字有事件 → 处理消息或连接关闭

相比 v1 版本的改进

  1. 并发处理能力:从单连接处理升级为多连接并发处理

  2. I/O 效率:使用 epoll 替代阻塞 I/O,提高处理效率

  3. 事件驱动:引入事件循环机制,实现真正的事件驱动架构

  4. 连接管理:完善的连接生命周期管理

  5. 可扩展性:更好的架构设计,为后续版本奠定基础

ReactorV3

类图

Reactor_v3

完整代码

说明

Reactor_v3 是基于 Reactor 模式的网络服务器实现的第三个版本,相比 v2 版本,引入了 TcpServer 高层抽象类,提供了更加简洁和易用的 API 接口,进一步封装了服务器的启动、停止和回调设置等操作。

核心特性

1. 高层抽象封装

2. 简洁的 API 设计

3. 保持原有优势

新增组件

TcpServer(TCP服务器)

工作流程

  1. 创建服务器实例

  2. 设置回调函数

  3. 启动服务器

  4. 停止服务器

内部工作流程

  1. 初始化阶段

    • TcpServer 创建 Acceptor 和 EventLoop 实例

    • Acceptor 准备监听套接字

    • EventLoop 初始化 epoll 实例

  2. 启动阶段

    • 调用 Acceptor::ready() 开始监听

    • 启动 EventLoop::loop() 进入事件循环

  3. 运行阶段

    • 监听新连接和消息事件

    • 自动分发到相应的回调函数

    • 管理连接的生命周期

  4. 停止阶段

    • 调用 EventLoop::unLoop() 退出事件循环

    • 自动清理资源

相比 v2 版本的改进

1. 更高层次的抽象

2. 更简洁的 API

3. 更好的封装性

4. 更易维护

ReactorV4

类图

Reactor_v4

完整代码

说明

Reactor_v4 是基于 Reactor 模式的网络服务器实现的第四个版本,相比 v3 版本,引入了 线程池(ThreadPool)任务队列(TaskQueue),实现了 Reactor + ThreadPool 的混合架构。这种设计将 I/O 处理和业务逻辑处理分离,显著提升了服务器的并发处理能力和性能。

核心特性

1. Reactor + ThreadPool 混合架构

2. 线程池管理

3. 任务队列

4. 跨线程通信

新增组件

ThreadPool(线程池)

TaskQueue(任务队列)

EventLoop(事件循环)- 增强版

TcpConnection(TCP连接)- 增强版

工作流程

1. 初始化阶段

2. 运行阶段

3. 请求处理流程

  1. 主线程(Reactor)

    • 监听网络事件(新连接、数据到达)

    • 接受新连接

    • 读取客户端数据

  2. 工作线程(ThreadPool)

    • 从任务队列获取业务处理任务

    • 执行具体的业务逻辑

    • 处理完成后通过 sendInLoop 发送结果

  3. 跨线程通信

    • 工作线程通过 eventfd 唤醒主线程

    • 主线程执行待处理任务(如发送响应)

    • 确保所有网络 I/O 在主线程中执行

技术实现

线程池实现

任务队列实现

跨线程通信

eventfd 通信机制

相比 v3 版本的改进

1. 并发处理能力大幅提升

2. 更好的资源利用

3. 更高的吞吐量

4. 更好的响应性

ReactorV5

类图

Reactor_v5

完整代码

说明

Reactor_v5 是基于 Reactor 模式的网络服务器实现的第五个版本,也是最终完善版本。相比 v4 版本,引入了 HeadServer 统一管理类,实现了线程池和网络服务器的完全整合,提供了一站式的高性能网络服务器解决方案。

核心特性

1. 一站式服务器解决方案

2. 完全整合的架构

3. 极简的使用方式

新增组件

HeadServer(主服务器)

MyTask(任务处理类)

工作流程

内部工作流程

  1. 初始化阶段

  2. 启动阶段

  3. 运行阶段

    • HeadServer 自动处理所有网络事件

    • 内置的回调函数处理连接管理

    • 自动分发任务到线程池

    • 透明的跨线程通信

  4. 停止阶段

技术实现

HeadServer 统一管理

标准化任务处理

自动回调绑定

相比 v4 版本的改进

1. 极大简化的使用方式

2. 更好的封装性

3. 更强的内聚性

4. 更好的可维护性

总结

架构演进

v1 → v2:引入事件循环

v2 → v3:增加高层抽象

v3 → v4:引入线程池

v4 → v5:完全整合

设计原则

1. 单一职责原则

2. 开闭原则

3. 依赖倒置原则

4. 接口隔离原则

性能特点

1. 高并发能力

2. 低资源消耗

3. 高吞吐量

4. 低延迟