原生 JDK 网络编程- NIO 之 Reactor 模式(七)

今天我们分享原生 JDK 网络编程- NIO 之 Reactor 模式:
1、“反应”器名字中”反应“的由来:
“反应”即“倒置”,“控制逆转” , 具体事件处理程序不调用反应器,而向反应器注 册一个事件处理器,表示自己对某些事件感兴趣,有时间来了,具体事件处理程序通过事件
处理器对某个指定的事件发生做出反应;这种控制逆转又称为“好莱坞法则”(不要调用我,让我来调用你)
例如,路人甲去做男士 SPA ,前台的接待小姐接待了路人甲,路人甲现在只对 10000 技 师感兴趣,但是路人甲去的比较早,就告诉接待小姐,等 10000 技师上班了或者是空闲了, 通知我。等路人甲接到接待小姐通知,做出了反应,把 10000 技师占住了。 然后,路人甲想起上一次的那个 10000 号房间不错,设备舒适,灯光暧昧,又告诉前台
的接待小姐,我对 10000 号房间很感兴趣,房间空出来了就告诉我,我现在先和 10000 这 个小姐聊下人生,10000 号房间空出来了,路人甲再次接到接待小姐通知,路人甲再次做出 了反应。路人甲就是具体事件处理程序,前台的接待小姐就是所谓的反应器,“10000 技师上班 了”和“10000 号房间空闲了”就是事件,路人甲只对这两个事件感兴趣,其他,比如 10001 号技师或者 10002 号房间空闲了也是事件,但是路人甲不感兴趣。 前台的接待小姐不仅仅服务路人甲 1 人,他还可以同时服务路人乙、丙 …….. ,每个人所 感兴趣的事件是不一样的,前台的接待小姐会根据每个人感兴趣的事件通知对应的每个人。
2、单线程 Reactor 模式流程:
①服务器端的 Reactor 是一个线程对象,该线程会启动事件循环,并使用 Selector( 选 择器) 来实现 IO 的多路复用。注册一个 Acceptor 事件处理器到 Reactor 中, Acceptor 事件处 理器所关注的事件是 ACCEPT 事件,这样 Reactor 会监听客户端向服务器端发起的连接请求 事件(ACCEPT 事件 )
② 客户端向服务器端发起一个连接请求, Reactor 监听到了该 ACCEPT 事件的发生并将 该 ACCEPT 事件派发给相应的 Acceptor 处理器来进行处理。 Acceptor 处理器通过 accept() 方 法得到与这个客户端对应的连接(SocketChannel) ,然后将该连接所关注的 READ 事件以及对 应的 READ 事件处理器注册到 Reactor 中,这样一来 Reactor 就会监听该连接的 READ 事件了。
③ 当 Reactor 监听到有读或者写事件发生时,将相关的事件派发给对应的处理器进行 处理。比如,读处理器会通过 SocketChannel read() 方法读取数据,此时 read() 操作可以直 接读取到数据,而不会堵塞与等待可读的数据到来。
④ 每当处理完所有就绪的感兴趣的 I/O 事件后, Reactor 线程会再次执行 select() 阻塞等 待新的事件就绪并将其分派给对应处理器进行处理。
注意, Reactor 的单线程模式的单线程主要是针对于 I/O 操作而言,也就是所有的 I/O 的 accept()、 read() write() 以及 connect() 操作都在一个线程上完成的。
但在目前的单线程 Reactor 模式中,不仅 I/O 操作在该 Reactor 线程上,连非 I/O 的业务 操作也在该线程上进行处理了,这可能会大大延迟 I/O 请求的响应。所以我们应该将非 I/O 的业务逻辑操作从 Reactor 线程上卸载,以此来加速 Reactor 线程对 I/O 请求的响应。

3、单线程 Reactor ,工作者线程池
与单线程 Reactor 模式不同的是,添加了一个工作者线程池,并将非 I/O 操作从 Reactor 线程中移出转交给工作者线程池来执行。这样能够提高 Reactor 线程的 I/O 响应,不至于因 为一些耗时的业务逻辑而延迟对后面 I/O 请求的处理。 使用线程池的优势:
① 通过重用现有的线程而不是创建新线程,可以在处理多个请求时分摊在线程创建和 销毁过程产生的巨大开销。
② 另一个额外的好处是,当请求到达时,工作线程通常已经存在,因此不会由于等待 创建线程而延迟任务的执行,从而提高了响应性。
③ 通过适当调整线程池的大小,可以创建足够多的线程以便使处理器保持忙碌状态。 同时还可以防止过多线程相互竞争资源而使应用程序耗尽内存或失败。 改进的版本中,所以的 I/O 操作依旧由一个 Reactor 来完成,包括 I/O accept() 、read()、 write()以及 connect() 操作。 对于一些小容量应用场景,可以使用单线程模型。但是对于高负载、大并发或大数据量 的应用场景却不合适,主要原因如下:
① 一个 NIO 线程同时处理成百上千的链路,性能上无法支撑,即便 NIO 线程的 CPU 负 荷达到 100% ,也无法满足海量消息的读取和发送;
② 当 NIO 线程负载过重之后,处理速度将变慢,这会导致大量客户端连接超时,超时 之后往往会进行重发,这更加重了 NIO 线程的负载,最终会导致大量消息积压和处理超时,成为系统的性能瓶颈;
4、多 Reactor 线程模式
Reactor 线程池中的每一 Reactor 线程都会有自己的 Selector 、线程和分发的事件循环逻 辑。mainReactor 可以只有一个,但 subReactor 一般会有多个。 mainReactor 线程主要负责接 收客户端的连接请求,然后将接收到的 SocketChannel 传递给 subReactor ,由 subReactor 来 完成和客户端的通信。
流程:
① 注册一个 Acceptor 事件处理器到 mainReactor 中, Acceptor 事件处理器所关注的事 件是 ACCEPT 事件,这样 mainReactor 会监听客户端向服务器端发起的连接请求事件 (ACCEPT 事件) 。启动 mainReactor 的事件循环。
② 客户端向服务器端发起一个连接请求, mainReactor 监听到了该 ACCEPT 事件并将该 ACCEPT 事件派发给 Acceptor 处理器来进行处理。 Acceptor 处理器通过 accept() 方法得到与这 个客户端对应的连接(SocketChannel) ,然后将这个 SocketChannel 传递给 subReactor 线程池。
subReactor 线程池分配一个 subReactor 线程给这个 SocketChannel ,即,将 SocketChannel 关注的 READ 事件以及对应的 READ 事件处理器注册到 subReactor 线程中。当 然你也注册 WRITE 事件以及 WRITE 事件处理器到 subReactor 线程中以完成 I/O 写操作。 Reactor 线程池中的每一 Reactor 线程都会有自己的 Selector 、线程和分发的循环逻辑。
④ 当有 I/O 事件就绪时,相关的 subReactor 就将事件派发给响应的处理器处理。注意, 这里 subReactor 线程只负责完成 I/O read() 操作,在读取到数据后将业务逻辑的处理放入 到线程池中完成,若完成业务逻辑后需要返回数据给客户端,则相关的 I/O write 操作还 是会被提交回 subReactor 线程来完成。
注意,所以的 I/O 操作 ( 包括, I/O accept() read() write() 以及 connect() 操作 ) 依旧还 是在 Reactor 线程 (mainReactor 线程 或 subReactor 线程 ) 中完成的。
Thread Pool( 线程池 ) 仅用 来处理非 I/O 操作的逻辑。多 Reactor 线程模式将“接受客户端的连接请求”和“与该客户端的通信”分在了两个 Reactor 线程来完成。 mainReactor 完成接收客户端连接请求的操作,它不负责与客户端的通 信,而是将建立好的连接转交给 subReactor 线程来完成与客户端的通信,这样一来就不会 因为 read() 数据量太大而导致后面的客户端连接请求得不到即时处理的情况。并且多 Reactor 线程模式在海量的客户端并发请求的情况下,还可以通过实现 subReactor 线程池来将海量
的连接分发给多个 subReactor 线程,在多核的操作系统中这能大大提升应用的负载和吞吐 量。
Netty 服务端使用了 Reactor 线程模式
5、和观察者模式的区别
观察者模式:
也可以称为为 发布 - 订阅 模式,主要适用于多个对象依赖某一个对象的状态并,当某 对象状态发生改变时,要通知其他依赖对象做出更新。是一种一对多的关系。当然,如果依 赖的对象只有一个时,也是一种特殊的一对一关系。通常,观察者模式适用于消息事件处理, 监听者监听到事件时通知事件处理者对事件进行处理(这一点上面有点像是回调,容易与反 应器模式和前摄器模式的回调搞混淆)。
Reactor 模式:
reactor 模式,即反应器模式,是一种高效的异步 IO 模式,特征是回调,当 IO 完成时, 回调对应的函数进行处理。这种模式并非是真正的异步,而是运用了异步的思想,当 IO 事 件触发时,通知应用程序作出 IO 处理。模式本身并不调用系统的异步 IO 函数。 reactor 模式与观察者模式有点像。不过,观察者模式与单个事件源关联,而反应器模
式则与多个事件源关联 。当一个主体发生改变时,所有依属体都得到通知。
 
原生 JDK 网络编程- NIO 之 Reactor 模式分析完成,下篇我们分析Netty的使用,敬请期待!
 
 
 
 
 
 
 
 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

寅灯

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值