Reactor和Netty模式详解

一、经典的I/O服务设计 ———— BIO模式

在这里插入图片描述

1、流程:

(1)、服务器端的Server是一个线程,通过死循环来阻塞的监听客户端的连接请求和通信。

(2)、当客户端向服务器端发起连接请求,服务器端通过ServerSocket.accept()阻塞
     返回一个Socket对象,此Socket对象和对应的客户端建立连接。

(3)、构建一个handler,将Socket传入该handler(处理程序),
 	 并创建一个线程来执行这个handler。

(4)、当该客户端和服务器端完成通信关闭连接后,这个线程就会被销毁。

(5)、然后服务器端Server继续执行accept()操作等待新的客户端连接请求。

2、总结:

服务器端有个主线程,通过死循环的方式监听客户端的连接请求,一旦有客户端连接请求,
服务器端的Server就通过ServerSocket.accept()
创建一个服务器端socket对象与客户端的socket对象连接。
然后构建一个handler(处理器),
这个处理器里面包含了处理逻辑和服务器端socket对象,
通过服务器端socket对象读取数据或推送数据,执行相应的逻辑。
一个handler是在一个新的线程里面执行的。
也就是一个连接请求开启一个线程,后续的read/write这些操作也都在这个线程中完成。

缺点:

可能有多数线程处于空闲状态,浪费资源。
线程之间切换,线程的开启销毁都是影响效率的。

二、NIO模式(多路IO复用模型)
在这里插入图片描述

1、流程

(1)、服务器端有个主线程,通过死循环的方式监听客户端的连接请求,
 	一旦有客户端连接请求,服务器端的Server就通过ServerSocket.accept()
 	创建一个服务器端socket对象与客户端的socket对象连接。

(2)、将socket对象设置为非阻塞,加入到一个集合里面。

(3)、开启一个线程,去循环监听这些非阻塞的socket对象,
 	 当某个socket对象准备好了数据,
 	 就开启一个线程去将系统内存的数据读取到用户线程内存里面。

2、总结

和同步非阻塞IO模型很像,只不过同步非阻塞IO是在用户线程里面去轮询socket
是否准备好数据,而多路复用IO是利用select调用系统内核在轮询。

三、单Reactor单线程模式

在这里插入图片描述

1、流程:

(1)、服务器端的Reactor对象实际上是个线程对象,
 这个线程循环监听非阻塞的socket对象,实际上是调用了系统内核的select函数。

(2)、注册一个Acceptor事件处理器到Reactor中,用于监听客户端的连接请求

(3)、收到客户端连接请求,Acceptor处理器通过accept()方法得到与客户端相对应的
  SocketChannel。

(4)、然后将该SocketChannel连接对应的READ事件处理器注册到Reactor中。

(5)、当Reactor监听到有读或者写事件发生时,将相关的事件派发给对应的处理器
 进行处理。

(6)、每当处理完所有就绪的感兴趣的I/O事件后,
 	Reactor线程会再次执行select()阻塞等待新的事件就绪并将其分派给
 	对应处理器进行处理。

2、总结

实际上就是对多路IO复用模型的封装,Reactor对象循环监听执行对应的回调函数,
将处理客户端连接的Acceptor注册到Reactor对象里面,
将该SocketChannel连接对应的READ事件处理器注册到Reactor对象里面

当Reactor对象监听到相关事件发生后,
就会执行之前已经注册的处理器的回调函数进行处理。
客户端所有的请求处理,不管是IO还是业务操作都在一个线程里面完成。

四、单Reactor多线程模式

在这里插入图片描述

流程:

1、和上面的单Reactor单线程模式差不多,区别就在于

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

总结:

将业务操作方法哦线程池里面去执行,缓解了IO处理的压力,不至于因为一些耗时的
业务逻辑而延迟对后面I/O请求的处理。

五、多Reactor多线程模式

在这里插入图片描述

和上面的单Reactor多线程模式差不多,只不过是进一步把客户端连接操作和其它的IO
操作再次拆分了,mainReactor线程处理连接操作,subReactor处理其它业务操作。

流程:

(1)、注册一个Acceptor事件处理器到mainReactor中,
 mainReactor去循环监听客户端连接事件。

(2)、mainReactor监听到客户端连接事件后,执行回调函数,生成SocketChannel。
     将SocketChannel传递给subReactor。

(3)、subReactor线程池分配线程给这个SocketChannel,这样就实现了subReactor线程
 对该SocketChannel IO操作的监听。

(4)、当监听到有I/O事件就绪时,该subReactor线程就会执行对应的read方法。
 注意,这里subReactor线程只负责完成I/O的read()操作,
 在读取到数据后,将业务逻辑的处理放入到线程池中完成。
 若完成业务逻辑后需要返回数据给客户端,
 则相关的I/O的write操作还是会被提交回subReactor线程来完成。

(5)、所以的I/O操作(包括,I/O的accept()、read()、write()以及connect()操作)
 依旧还是在Reactor线程(mainReactor线程 或 subReactor线程)中完成的。
 Thread Pool(线程池)仅用来处理非I/O操作的逻辑。

总结:

分离了客户端连接请求和其它IO请求,
解决了因为read()数据量太大而导致后面的客户端连接请求得不到即时处理的情况。

并且多Reactor线程模式在海量的客户端并发请求的情况下,
还可以通过实现subReactor线程池来将海量的连接分发给多个subReactor线程,
在多核的操作系统中这能大大提升应用的负载和吞吐量。

六、Netty模式

Netty框架实际上就是实现了多Reactor多线程模式

mainReactor ———— bossGroup(NioEventLoopGroup) 中的某个NioEventLoop

subReactor ———— workerGroup(NioEventLoopGroup) 中的某个NioEventLoop

acceptor ———— ServerBootstrapAcceptor

ThreadPool ———— 用户自定义线程池

1、流程:

(1)、服务器端有两个线程池,(事件循环组),bossGroup负责监听客户端连接,
 wokerGroup负责监听网络读写。

(2)、每个线程池组里面都含有多个NioEventLoop(事件循环线程),
 就是一个死循环,不停的监听。

(3)、每个NioEventLoop(事件循环线程)中有两个对象,
 一个是selector,负责监听绑定在其上的socket的IO事件,
 一个是taskQueue,负责来存放没来得及处理的IO事件。

(4)、bossGroup里面的NioEventLoop监听到了客户端连接事件,做以下三个操作:

	 <1>、轮询accpet事件(客户端连接事件)。
	
	 <2>、监听到了连接请求,就通过accpet(),生成一个nioSocketChannel
	  	 (连接通道),将其注册到workGroup里面的某个NioEventLoop的selector
	
	 <3>、继续处理任务队列。

(5)、workGroup里面的NioEventLoop监听到了连接事件,做以下三个操作:

  <1>、轮询每个NioEventLoop中的selector是否有read、write事件发生

  <2>、监听到了事件就执行,利用selector上绑定的niosocketchannel
      对应的pipeline中的channelhandler进行处理。
  
  <3>、继续处理任务队列。

(6)、一个niosocketchannel会对应一个pipeline,
    pipeline(管道)里面有多个channelhandler(处理器),
    也就是通过一系列channel(通道)里绑定的handler进行IO处理。
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值