1、Netty里的Reactor模型如下所示
select->process->run
理解起来比较简单,先select要处理的事件,然后处理这些事件,然后处理处理这些事件时添加的任务
2、select操作
每次轮询的开始,Netty先计算策略
Netty会先判断当前是否有需要执行的任务,如果有,就直接selectNow,如果没有,就返回SELECT,表示执行阻塞select
如果返回的是SELECT,则说明要执行阻塞select
这里会先得到下一个延时任务最晚执行的时间,拿到这个时间之后,如果当前没有要执行的任务,就执行一下阻塞select
如果后面没有要执行的定时任务,就会一直阻塞直到select到一个感兴趣的事件
如果又要执行的定时任务,就会计算5毫秒后是否会到定时任务开始执行的时间,如果是,就会直接select,如果不是,就会执行带超时时间的阻塞select
select是个io操作,有可能会抛出ioexception,当抛出ioexception时,netty会重新构建selector,然后处理异常,这里处理异常的方式打印一条日志,并当前线程睡眠1秒,防止造成cpu过高
3、process操作
需要处理的事件select出来之后,就要进行处理了
netty会先得到当前ioRatio
1)当ioRation是100时,说明io任务是最重要的,这是如果有selectKey要处理,就会进行处理,然后执行要处理的task
2)如果介于0~100之间,netty会计算process执行的时间,那这个执行的时间计算任务应该执行的时间
3)如果小于等于0,说明不需要执行io操作,这里会直接执行任务
进入processSelectedKeys,我们会发现里面有两个实现,如果selectKey不是空,就执行优化处理方式,如果是空,就直接处理
我们点一下selectKeys可以发现,这是一个netty自己实现的set,没错,优化主要就是这个地方,netty使用一个自己实现的set,结合使用场景,会比jdk提供的set有更好的处理性能
我们打开优化的处理逻辑,发现遍历selectKey,拿到selectKey上绑定的对象,我们在注册的时候,会把NioSocketChannel绑定到对应的selectKey上
这里有两个细节:1、拿到selectKey后会把set里的元素置为空,方便后面垃圾回收,2、当selectKey被取消的时候,会再执行一次select
然后进入processSelectedKey
processSelectedKey会拿到准备好的操作参数,调用unsafe去执行对应的读写操作
4、run操作
这里会先从延时队列需要执行的任务,添加到任务队列,任务队列会一个个执行任务,每执行一个任务,就会判断当前时间是否超时,如果超时,就不再执行
5、后续处理
执行到最后,netty会判断ranTasks和select的selectorKey是否大于0,如果不是,说明这次轮询是一次空轮询
如果空轮询的次数到达512(默认)次,netty会认为触发了jdk nio的空轮询bug,这时netty会重建selector
netty会新建一个selector,netty会在这里作废老的selectKey,在之前的channel上用新的selector注册新的selectKey,然后关闭老的selector
总结
1、netty先执行select,然后处理select出来的selectionKey,然后处理任务
2、netty在select遇到ioexception或遇到连续多次的空轮询时,会重建对应的selector
3、问题,netty执行的任务是什么时候被添加进去的,是怎样保持线程安全的