Netty 源码分析 05 EventLoop

在看 EventLoop 的具体实现之前,我们先来对 Reactor 模型做个简单的了解

我们来看看 Reactor 模型的核心思想

初步一看,Java NIO 符合 Reactor 模型啊?因为 Reactor 有 3 种模型实现:

  1. 单 Reactor 单线程模型
  2. 单 Reactor 多线程模型
  3. 多 Reactor 多线程模型

第三种模型比起第二种模型,是将 Reactor 分成两部分:

  1. mainReactor 负责监听 ServerSocketChannel ,用来处理客户端新连接的建立,并将建立的客户端的 SocketChannel 指定注册给 subReactor 。
  2. subReactor 维护自己的 Selector ,基于 mainReactor 建立的客户端的 SocketChannel 多路分离 IO 读写事件,读写网络数据。对于业务处理的功能,另外扔给 worker 线程池来完成。
  1. mainReactor 主要用来处理网络 IO 连接建立操作,通常,mainReactor 只需要一个,因为它一个线程就可以处理。
  2. subReactor 主要和建立起来的客户端的 SocketChannel 做数据交互和事件业务处理操作。通常,subReactor 的个数和 CPU 个数相等,每个 subReactor 独占一个线程来处理。

此种模式中,每个模块的工作更加专一,耦合度更低,性能和稳定性也大大的提升,支持的可并发客户端数量可达到上百万级别

  • 对于 Netty NIO 客户端来说,仅创建一个 EventLoopGroup 。
  • 一个 EventLoop 可以对应一个 Reactor 。因为 EventLoopGroup 是 EventLoop 的分组,所以对等理解,EventLoopGroup 是一种 Reactor 的分组。

那么 Netty NIO 客户端是否能够使用【多 Reactor 多线程模型】呢?😈 创建多个 Netty NIO 客户端,连接同一个服务端。那么多个 Netty 客户端就可以认为符合多 Reactor 多线程模型了

 

对于 Netty NIO 服务端来说,创建两个 EventLoopGroup 。

  • bossGroup 对应 Reactor 模式的 mainReactor ,用于服务端接受客户端的连接。比较特殊的是,传入了方法参数 nThreads = 1 ,表示只使用一个 EventLoop ,即只使用一个 Reactor 。这个也符合我们上面提到的,“通常,mainReactor 只需要一个,因为它一个线程就可以处理”。
  • workerGroup 对应 Reactor 模式的 subReactor ,用于进行 SocketChannel 的数据读写。对于 EventLoopGroup ,如果未传递方法参数 nThreads ,表示使用 CPU 个数 Reactor 。这个也符合我们上面提到的,“通常,subReactor 的个数和 CPU 个数相等,每个 subReactor 独占一个线程来处理”。

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

EventLoop(二)之 EventLoopGroup

6. EventLoopGroup

io.netty.channel.EventExecutorGroup ,继承 EventExecutorGroup 接口,EventLoop 的分组接口

7. MultithreadEventLoopGroup

io.netty.channel.MultithreadEventLoopGroup ,实现 EventLoopGroup 接口,继承 MultithreadEventExecutorGroup 抽象类,基于多线程的 EventLoop 的分组抽象类。

7.5 register

#register() 方法,注册 Channel 到 EventLoopGroup 中。实际上,EventLoopGroup 会分配一个 EventLoop 给该 Channel 注册

8. NioEventLoopGroup

io.netty.channel.nio.NioEventLoopGroup ,继承 MultithreadEventLoopGroup 抽象类,NioEventLoop 的分组实现类。

 

调用rebuildSelector,重建selector对象

+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

EventLoop(三)之 EventLoop 初始化

7. AbstractScheduledEventExecutor

io.netty.util.concurrent.AbstractScheduledEventExecutor ,继承 AbstractEventExecutor 抽象类,支持定时任务的 EventExecutor 的抽象类。

8. SingleThreadEventExecutor

io.netty.util.concurrent.SingleThreadEventExecutor ,实现 OrderedEventExecutor 接口,继承 AbstractScheduledEventExecutor 抽象类,基于单线程的 EventExecutor 抽象类,即一个 EventExecutor 对应一个线程

+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

EventLoop(四)之 EventLoop 运行

2. NioEventLoop

io.netty.channel.nio.NioEventLoop ,继承 SingleThreadEventLoop 抽象类,NIO EventLoop 实现类,实现对注册到其中的 Channel 的就绪的 IO 事件,和对用户提交的任务进行处理。

ioRatio 属性,在 NioEventLoop 中,会三种类型的任务:1) Channel 的就绪的 IO 事件;2) 普通任务;3) 定时任务。而 ioRatio 属性,处理 Channel 的就绪的 IO 事件,占处理任务的总时间的比例。

2.9 run

#run() 方法,NioEventLoop 运行,处理任务。这是本文最重要的方法

我们知道 SelectStrategy#calculateStrategy(...) 方法,有 3 种返回的情况

第一种,SelectStrategy.CONTINUE ,默认实现下,不存在这个情况。 值为-2 代表要进行重试

第二种,SelectStrategy.SELECT ,进行 Selector 阻塞 select 。  值为 -1

第三种,>= 0 ,已经有可以处理的任务,直接向下

总的来说,#run() 的执行过程,就是如下一张图:

run

2.12 select

#select(boolean oldWakenUp) 方法,选择( 查询 )任务。这是本文最重要的方法

获得 select 操作的计数器。主要用于记录 Selector 空轮询次数,所以每次在正在轮询完成( 例如:轮询超时 ),则重置 selectCnt 为 1 。

第 74 至 93 行:不符合 select 超时的提交,若 select 次数到达重建 Selector 对象的上限,进行重建。这就是 Netty 判断发生 NIO Selector 空轮询的方式,N ( 默认 512 )次 select 并未阻塞超时这么长,那么就认为发生 NIO Selector 空轮询。过多的 NIO Selector 将会导致 CPU 100%

+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

EventLoop(五)之 EventLoop 处理 IO 事件

3. openSelector

#openSelector() 方法,创建 Selector 对象

4. SelectedSelectionKeySet

io.netty.channel.nio.SelectedSelectionKeySet ,继承 AbstractSet 抽象类,已 select 的 NIO SelectionKey 集合。代码如下:

5. SelectedSelectionKeySetSelector

io.netty.channel.nio.SelectedSelectionKeySetSelector ,基于 Netty SelectedSelectionKeySet 作为 selectionKeys 的 Selector 实现类

select 相关的 3 个方法,在调用对应的 Java NIO Selector 方法之前,会调用 SelectedSelectionKeySet#reset() 方法,重置 selectionKeys 。从而实现,每次 select 之后,都是新的 select 的 NIO SelectionKey 集合。

6. rebuildSelector

#rebuildSelector() 方法,重建 Selector 对象

   总的来说,#rebuildSelector() 方法,相比 #openSelector() 方法,主要是需要将老的 Selector 对象的“数据”复制到新的 Selector 对象上,并关闭老的 Selector 对象。

7. processSelectedKeys

在 #run() 方法中,会调用 #processSelectedKeys() 方法,处理 Channel 新增就绪的 IO 事件

7.1 processSelectedKeysOptimized

#processSelectedKeysOptimized() 方法,基于 Netty SelectedSelectionKeySetSelector ,处理 Channel 新增就绪的 IO 事件

7.2 processSelectedKeysPlain

#processSelectedKeysOptimized() 方法,基于 Java NIO 原生 Selecotr ,处理 Channel 新增就绪的 IO 事件

7.3 processSelectedKey

#processSelectedKey(SelectionKey k, AbstractNioChannel ch) 方法,处理一个 Channel 就绪的 IO 事件

8.1 register

#register(final SelectableChannel ch, final int interestOps, final NioTask<?> task) 方法,注册 Java NIO Channel ( 不一定需要通过 Netty 创建的 Channel )到 Selector 上,相当于说,也注册到了 EventLoop 上

8.2 invokeChannelUnregistered

#invokeChannelUnregistered(NioTask<SelectableChannel> task, SelectionKey k, Throwable cause) 方法,执行 Channel 取消注册

8.3 processSelectedKey

#processSelectedKey(SelectionKey k, NioTask<SelectableChannel> task) 方法,使用 NioTask ,自定义实现 Channel 处理 Channel IO 就绪的事件

代码比较简单,胖友自己看中文注释。主要是看懂 state 有 3 种情况:

  • 0 :未执行。
  • 1 :执行成功。
  • 2 :执行异常。

+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

EventLoop(六)之 EventLoop 处理普通任务

2. runAllTasks 带超时

在 #run() 方法中,会调用 #runAllTasks(long timeoutNanos) 方法,执行所有任务直到完成所有,或者超过执行时间上限

3. runAllTasks

在 #run() 方法中,会调用 #runAllTasks() 方法,执行所有任务直到完成所有

4. pollTask

#pollTask() 方法,获得队头的任务

5. afterRunningAllTasks

在 《精尽 Netty 源码解析 —— EventLoop(三)之 EventLoop 初始化》 的 「9.10 afterRunningAllTasks」 中,#afterRunningAllTasks() 方法,执行所有任务完成的后续方法

+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

EventLoop(七)之 EventLoop 处理定时任务

2. ScheduledFutureTask

io.netty.util.concurrent.ScheduledFutureTask ,实现 ScheduledFuture、PriorityQueueNode 接口,继承 PromiseTask 抽象类,Netty 定时任务。

3. AbstractScheduledEventExecutor

io.netty.util.concurrent.AbstractScheduledEventExecutor ,继承 AbstractEventExecutor 抽象类,支持定时任务的 EventExecutor 的抽象类。

3.2 scheduledTaskQueue

#scheduledTaskQueue() 方法,获得定时任务队列。若未初始化,则进行创建

3.4 schedule

#schedule(final ScheduledFutureTask<V> task) 方法,提交定时任务

4. SingleThreadEventExecutor

在 《精尽 Netty 源码解析 —— EventLoop(六)之 EventLoop 处理普通任务》 中,有个 #fetchFromScheduledTaskQueue() 方法,将定时任务队列 scheduledTaskQueue 到达可执行的任务,添加到任务队列 taskQueue 中

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

EventLoop(八)之 EventLoop 优雅关闭

我们总结一下,调用shutdown()方法从循环跳出的条件有:
(1).执行完普通任务
(2).没有普通任务,执行完shutdownHook任务
(3).既没有普通任务也没有shutdownHook任务
调用shutdownGracefully()方法从循环跳出的条件有:
(1).执行完普通任务且静默时间为0
(2).没有普通任务,执行完shutdownHook任务且静默时间为0
(3).静默期间没有任务提交
(4).优雅关闭截止时间已到

 

可知,Netty默认的shutdownGracefully()机制为:在2秒的静默时间内如果没有任务,则关闭;否则15秒截止时间到达时关闭。换句话说,在15秒时间段内,如果有超过2秒的时间段没有任务则关闭。至此,我们明白了从EvnetLoop循环中跳出的机制,最后,我们抵达终点站:线程结束机制。这一部分的代码实现在线程工厂的生成方法中:

 

 

 

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
蛋白质是生物体中普遍存在的一类重要生物大分子,由天然氨基酸通过肽键连接而成。它具有复杂的分子结构和特定的生物功能,是表达生物遗传性状的一类主要物质。 蛋白质的结构可分为四级:一级结构是组成蛋白质多肽链的线性氨基酸序列;二级结构是依靠不同氨基酸之间的C=O和N-H基团间的氢键形成的稳定结构,主要为α螺旋和β折叠;三级结构是通过多个二级结构元素在三维空间的排列所形成的一个蛋白质分子的三维结构;四级结构用于描述由不同多肽链(亚基)间相互作用形成具有功能的蛋白质复合物分子。 蛋白质在生物体内具有多种功能,包括提供能量、维持电解质平衡、信息交流、构成人的身体以及免疫等。例如,蛋白质分解可以为人体提供能量,每克蛋白质能产生4千卡的热能;血液里的蛋白质能帮助维持体内的酸碱平衡和血液的渗透压;蛋白质是组成人体器官组织的重要物质,可以修复受损的器官功能,以及维持细胞的生长和更新;蛋白质也是构成多种生理活性的物质,如免疫球蛋白,具有维持机体正常免疫功能的作用。 蛋白质的合成是指生物按照从脱氧核糖核酸(DNA)转录得到的信使核糖核酸(mRNA)上的遗传信息合成蛋白质的过程。这个过程包括氨基酸的活化、多肽链合成的起始、肽链的延长、肽链的终止和释放以及蛋白质合成后的加工修饰等步骤。 蛋白质降解是指食物中的蛋白质经过蛋白质降解酶的作用降解为多肽和氨基酸然后被人体吸收的过程。这个过程在细胞的生理活动中发挥着极其重要的作用,例如将蛋白质降解后成为小分子的氨基酸,并被循环利用;处理错误折叠的蛋白质以及多余组分,使之降解,以防机体产生错误应答。 总的来说,蛋白质是生物体内不可或缺的一类重要物质,对于维持生物体的正常生理功能具有至关重要的作用。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值