Netty学习——Reactor线程模型

本文详述了从传统阻塞I/O线程模型到Reactor线程模型的演进,包括单Reactor单线程、单Reactor多线程及主从Reactor多线程模型的工作原理与优缺点,最后解析了Netty如何基于主从Reactor模型进行优化。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、 线程模型

1.1 介绍

1.目前的线程模型

  • 传统阻塞I/O线程模型
  • Reactor线程模型

Reactor线程模型分类

  • 单Reactor单线程
  • 单Reactor多线程
  • 主从Reactor多线程

Netty线程模型,是基于主从Reactor多线程模型的进行了改造而产生的

1.2 传统阻塞I/O线程模型

最早的模型是中,是用一个while循环来监听端是否有新的链接。如下图代码所示:

while(true){
    socket = accept(); //阻塞,接收连接
    //*** handle 操作
    socket.getInputStream().read();//读取数据
    byte[] data;//处理业务
    socket.getOutputStream().write(data);//写入结果
}

这种一个很大的问题,就是一旦handle阻塞来。那么后面的链接请求就全部比阻塞来。导致服务的吞吐量特别的低。为了防止这种阻塞问题,有出现了了一个线程处理一个链接的模式,如图所示
传统阻塞I/O服务模型
模型的特点

  1. 采用阻塞IO模式获取输入的数据
  2. 每个链接都需要独立的线程完成数据的输入、业务处理和数据返回
    大致的伪代码如下:
while(true){
    socket = accept(); //阻塞,接收连接
    new Thrad(new Handle(socket)).start ;   //读取数据、业务处理、写入结果
}
class Handle  implements Runable{
	void run{
		 socket.getInputStream().read();//读取数据
    	byte[] data;//处理业务
    	socket.getOutputStream().write(data);//写入结果
	}
}

模型导致的问题
3. 当并发数很大的时候,会创建大量的线程,占有很大的系统资源 (比较线程是稀缺资源)
4. 线程创建后,如果没有数据可读,该线程就会阻塞在read操作上,操作线程资源的浪费

1.3 Reactor线程模型

基本概述

  • 基于I/O复用模型,多个链接对象共用一个阻塞对象,应用程序只需要在一个对象上阻塞等待,无需阻塞等待所有的链接。
  • 基于线程池复用线程资源,将链接完成的业务处理交给线程池处理
    如下图所示
    在这里插入图片描述
    说明:
  • reactor模式,通过一个或多个输入同时传递给服务器的模式(基于事件驱动)
  • 服务器处理器 eventDispatch(也就是reactor)在一个单独的线程中进行,负责监听和分发事件,具体的业务处理有它分派给对应的处理线程。因此Reactor模式也叫Dispatch模式
  • Reactor模式使用了IO复用监听事件,收到事件后,分发给某个线程。
1.4 单Reactor单线程模型

在这里插入图片描述

  1. selector 是NIO中的I/O 复用模型的标准网络api,可以实现应用程序通过一个阻塞对象监听多路链接请求
  2. Reactor对象通过Selector监控客户端的请求事件,然后进行分发
  3. 如果是建立连接事件,则有Acceptor通过accept,注册一个SelectionKey.OP_READ的连接channel
  4. 如果不是建立连接事件。则Reactor会分发调用不同的Handler来详细
  5. Handler会完成 read -> 业务处理 -> send的完整业务流程
    服务端用一个线程通过多路复用搞定所有的IO操作(包括连接、读写等)。逻辑清晰。但是当来连接请求过多的时候,将无法支撑
    对于Selector不了解的 可以访问 Netty学习——BIO、NIO、AIO的总结

优点

  • 模型简单,没有多线程,进程的通信、竞争的问题。全部都在一个线程中完成

缺点

  • 性能问题,只有一个线程,无法发挥多核CPU的新年。Handler在处理某个连接上的业务时,无法处理其他的连接事件,是性能瓶颈
  • 可靠性问题,线程意外终止,或者进入死循环,将导致整个系统通信不可用。

使用场景

  • 客户端数量不多,业务处理快。比如redis在业务处理的事件复杂度为O(1)的情况
1.5 单Reactor多线程模型

在这里插入图片描述

  • reactor对象通过Selector监控客户端的请求事件,收到事件之后,通过dispatch进行分发
  • 如果建立连接的请求,则由Acceptor通过accept处理请求,并创建一个handler对象处理完成连接的各种事件
  • 如果不是连接请求,则由reactor分发调用连接对应的handler来处理
  • handler只负责响应事件,不做具体的业务处理,通过read读取数据后,会分发给后面的worker线程池来执行
  • worker线程池会分配一个空闲的线程去完成真正的业务,并将结果返回给handler
  • handler收到响应后,通过send将结果返回给client

优点

  • 可以充分发挥多核CPU的处理能力

缺点

  • 多线程访问共享数据比较复杂
  • reactor需要处理所有事件的监听和响应,单线程情况下,如果在高并发场景下容易出现性能问题
1.6 主从Reactor多线程模型在这里插入图片描述
  • Reactor主线程 MainReactor对象通过select监听连接事件,收到事件后,通过Acceptor处理连接请求
  • 当Acceptor处理连接事件后,MainReactor将连接分配给SubReactor
  • subReactor将连接加入到连接队列进行监听,并创建handler进行各种事件处理
  • 当有新事件发生时,subReactor就会调用对应当handler处理
  • handler只负责响应事件,不做具体的业务处理,通过read读取数据后,会分发给后面的worker线程池来执行
  • worker线程池会分配一个空闲的线程去完成真正的业务,并将结果返回给handler
  • handler收到响应后,通过send将结果返回给client
  • Reactor主线程可以对应多个Reactor子线程

优点

  • 父线程和子线程的数据交互简单,职责明确,父线程只需要接收新连接,子线程完成后续的业务处理
  • 父线程和子线程的数据交互简单,Reactor主线程只需要吧连接传给子线程,子线程无需返回数据。

缺点

  • 编程复杂度比较高

使用场景
这种模型在许多项目中广泛使用,包括Nginx主从Reactor多进程模型,Memcached主从多线程等

2.7 Netty模型

在这里插入图片描述

  • Netty抽象出两组线程吃,BossGroup专门负责接收客户端的连接,WorkerGroup专门负责网络的读写
  • BoosGroup和WorkerGroup的类型都是 NioEventLoopGroup。
  • NioEventLoopGroup相当于一个事件循环组,这个组中含有多个事件循环,每个事件循环是NioEventLoop
  • NioEventLoop表示一个不断循环的执行处理任务的线程,每个NioEventLoop都有一个selector,用于监听绑定在其上的socker网络通信
  • NioEventLoopGroup可以有多个线程,即可以含有多个NioEventLoop
  • 每个BossNioEventLoop循环执行的步骤是
    • 轮询accept事件
    • 处理accept事件,和client建立连接,生成NioSocketChannel 并将其注册到WorkerGroup中的某个Selector上
    • 处理任务队列的任务,即runAllTask
  • 每个WorkerNioEventLoop循环执行的步骤
    • 轮询 read/write事件
    • 处理io事件,即read、write事件。在对应的NioSocketChannel处理
    • 处理任务队列的任务,即runAllTasks
  • 每个workerNIOEventLoop处理业务时候,会使用pipeline(管道),pipeline中包含了channel,即每个pipeline可以获取到对应的管道,管道中维护了很多的处理器
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值