目录
随着计算机硬件性能不断提高,服务器 CPU 的核数越来越越多,为了充分利用多核 CPU 的处理能力,提升系统的处理效率和并发性能,多线程并发编程越来越显得重要。无论是 C++ 还是 Java 编写的网络框架,大多数都是基于 Reactor 模式进行设计和开发,Reactor 模式基于事件驱动,特别适合处理海量的 I/O 事件,今天我们就简单聊聊 Reactor 线程模型,主要内容分为以下几个部分:
- 经典的 I/O 通信模型;
- Reactor 线程模型详述;
- Reactor 线程模型几种模式;
- Netty Reactor 线程模型的实践;
文章相关视频讲解:
reactor的5种实现方式,单线程、多线程、多核、多进程的实现
Linux后端开发网络底层原理知识学习点击观看:c/c++Linux后台服务器开发高级架构师视频资料
IO 通信模型
我们先要来谈谈 I/O 通信。说到 I/O 通信,往往会提到同步(synchronous)I/O 、异步(asynchronous)I/O、阻塞(blocking)I/O 和非阻塞(non-blocking)I/O 四种。有关同步、异步、阻塞和非阻塞的区别很多时候解释不清楚,不同的人知识背景不同,对概念很难达成共识。本文讨论的背景是 Linux 环境下的 Network I/O。
一次 I/O 过程分析
对于一次 Network I/O (以 read 举例),它会涉及到两个系统对象,一个是调用这个 I/O 的进程或线程,另一个就是系统内核 (kernel)。当一个 read 操作发生时,会经历两个阶段(记住这两个阶段很重要,因为不同 I/O 模型的区别就是在两个阶段上各有不同的处理):
- 第一个阶段:等待数据准备 (Waiting for the data to be ready);
- 第二个阶段:将数据从内核拷贝到进程中 (Copying the data from the kernel to the process);
五种 I/O 模型
Richard Stevens 的《UNIX® Network Programming Volume》提到了 5 种 I/O 模型:
- Blocking I/O (同步阻塞 I/O)
- Nonblocking I/O(同步非阻塞 I/O)
- I/O multiplexing(多路复用 I/O)
- Signal driven I/O(信号驱动 I/O,实际很少用,Java 不支持)
- Asynchronous I/O (异步 I/O)
接下来我们对这 5 种 I/O 模型进行说明和对比。
Blocking I/O
在 Linux 中,默认情况下所有的 Socket 都是 blocking 的,也就是阻塞的。一个典型的读操作时,流程如图:
当用户进程调用了 recvfrom 这个系统调用, 这次 I/O 调用经历如下 2 个阶段:
- 准备数据: 对于网络请求来说,很多时候数据在一开始还没有到达(比如,还没有收到一个完整的 UDP 包),这个时候 kernel 就要等待足够的数据到来。而在用户进程这边,整个进程会被阻塞。
- 数据返回:kernel 一但等到数据准备好了,它就会将数据从 kernel 中拷贝到用户内