传统阻塞 I/O 服务模型
传统阻塞 I/O
服务模型采用阻塞式I/O
获取输入的数据,每个连接都需要独立的线程完成数据的输入、业务的处理、以及数据的返回;因此会发生两个问题:(1).当并发数很大时,就会创建大量的线程,占用较大的系统资源;(2).连接创建后,如果当前线程暂时没有数据可读,该线程会阻塞在 Handler对象中的read
操作,导致资源浪费;
Reactor 模式服务模型
反应器 Reactor
模式基于 I/O
复用模型,多个连接共用一个ServiceHandler
阻塞对象,程序无需阻塞等待所有连接,这样就减少了阻塞时间;当多个客户端进行连接时,先把连接请求交给ServiceHandler
,ServiceHandler
把请求分派到对应的EventHandler
进行处理;Reactor
模式根据Reactor
的数量和处理资源池线程的数量不同分为三种实现:单 Reactor
单线程、单 Reactor
多线程、主从 Reactor
多线程;
1.单 Reactor 单线程模型
单 Reactor
单线程模型实现中Reactor
对象通过 Select
监控客户端请求事件,收到事件后通过 Dispatch
进行分发;若此时有客户端请求:首先判断请求类型;如果是建立连接请求事件,则由 Acceptor
通过 accept
处理连接请求,然后创建一个 Handler
对象处理连接完成后的后续业务处理;如果不是建立连接事件,则 Reactor
会分发调用连接对应的 Handler
来处理(这里的Handler
负责事件的响应和业务的处理);这种实现中所有请求的所有操作通过一个线程处理很容易导致性能瓶颈;
2.单 Reactor 多线程模型
单Reactor
多线程模型实现中Reactor
对象通过 Select
监控客户端请求事件,收到事件后,通过 Dispatch
进行分发;若此时有客户端请求:首先判断请求类型;如果是建立连接请求事件,则由 Acceptor
通过 accept
处理连接请求,然后创建一个 Handler
对象处理连接完成后的后续业务处理;如果不是建立连接事件,则 Reactor
会分发调用连接对应的 Handler
来处理(这里handler
负责响应事件,不做具体的业务处理),Handler
通过 read
读取数据后,会分发给后面的 worker
线程池,worker
线程池会分配独立线程完成真正的业务,并将结果返回给 handler
;这种实现中Reactor
在主线程中运行并且承担了所有事件的监听和响应,主线程承担了过多的任务,高并发场景下也容易导致性能瓶颈;
3.主从 Reactor 多线程模型
主从Reactor
多线程模型实现中,Reactor
主线程中的 MainReactor
对象通过 select
监听连接事件;若此时有客户端请求:MainReactor
收到事件后就通过 Acceptor
处理连接事件;当 Acceptor
处理连接事件后,MainReactor
会将连接分配给 SubReactor
(MainReactor
和SubReactor
是一对多的模型);subreactor
将连接加入到连接队列进行监听,并创建 handler
进行各种事件处理;当有新事件发生时,subreactor
就会调用对应的 handler
处理;handler
通过 read
读取数据,分发给后面的 worker
线程处理;worker
线程池分配独立的 worker
线程进行业务处理,并返回结果,handler
收到响应的结果后,再通过 send
将结果返回给 client
;
Netty Reactor服务模型
Netty
抽象出两组线程池 :BossGroup
和 WorkerGroup
,其中BossGroup
专门负责接收客户端的连接,WorkerGroup
专门负责网络的读写;BossGroup
和 WorkerGroup
中对象的类型都是 NioEventLoopGroup
,NioEventLoopGroup
相当于一个事件循环组,这个组中含有多个事件循环对象NioEventLoop
;NioEventLoop
表示一个不断循环的执行处理任务的线程,每个 NioEventLoop
都有一个 Selector
,用于监听绑定在其上的 socket
的网络通讯;
每个 BossGroup
下面的NioEventLoopGroup
中的NioEventLoop
循环执行的步骤有 3
步:(1)轮询 accept
事件;(2)处理 accept
事件,与 client
建立连接,生成 NioScocketChannel
,并将其注册到某个 workerGroup
的 NIOEventLoop
上的 Selector
上;(3)继续处理任务队列的任务,即 runAllTasks
每个 WorkerGroup
下面的NioEventLoopGroup
中的NIOEventLoop
循环执行的步骤:(1)轮询 read
,write
事件;(2)处理 I/O
事件,即 read
,write
事件,在对应 NioScocketChannel
处理;(3)处理任务队列的任务,即 runAllTasks