Netty源码(二):三种Reactor模式

1. Reactor模型

1.1 单线程reactor

在这里插入图片描述
一个单线程去维护所有的acceptor、read、decode、compute、encode、send。

1.2 多线程reactor模型

在这里插入图片描述
我们认为decode、read、compute比较耗时,使用一个线程池去管理。

1.3 主从多线程模型

在这里插入图片描述
上面有两个reactor,因为所有事件里面acceptor最重要,用一个单独的reactor去完成这个事件剩下的事件和1.2就比较相似了。

三种reactor模式的使用。

在这里插入图片描述

2. 问题一:Netty如何支持主从reactor模型

57行的b.group(bossGroup, workerGroup)就是上面说的主从reactor模式
在这里插入图片描述
跟进源码发现首先是调用了父类的super.group(parentGroup)方法,然后是在88行初始化了ServerBootstrap类中的全局变量childGroup
在这里插入图片描述

2.1 跟进group方法的81行,查看parentGroup

跟进81行代码,进入父类的group()方法,发现这个方法只是初始化了AbstractBootstrap类中的group全局变量
在这里插入图片描述
继续在AbstractBootstrap类中搜索group的使用,发现326行中出现了,将ServerSocketChannel绑定到parentGroup上的操作(parentGroup也就是workGroup)。

在这里插入图片描述

2.2 回到ServerBootstrap类中查看childGroup

void init(Channel channel)方法中
1.childGroup被当作参数赋值给了currentChildGroup
2.currentChildGroup又被当作参数传入了ServerBootstrapAcceptor
在这里插入图片描述
跟进159行代码,ServerBootstrapAcceptorServerBootstrap的一个内部类1.ServerBootstrapAcceptor内部也有一个childGroup
2.在ServerBootstrapAcceptor的构造函数中,传入的childGroup被当作参数赋值给了
ServerBootstrapAcceptor内部的全局变量childGroup
在这里插入图片描述
ServerBootstrapAcceptor中有一个channelRead()方法,里面将某个名为child的channel注册到了childGroup
在这里插入图片描述
总结:bossGroup和workGroup分别注册了不同的channel在上面,以此完成一个主从reactor模型

3. 问题二:为什么main reactor大都只能用到一个线程

AbstractBootstrap保存了名叫group的对象保存的是是parentGroup的全局变量
在这里插入图片描述
而通过AbstractBootstrapgroup()方法可以返回parentGroup变量
在这里插入图片描述
最后发现查看group方法的调用栈,发现会调用被doBind方法调用,而我们channel初始化只会绑定一次端口,所以我们需要只需要使用到一个线程。
在这里插入图片描述

4. 问题三:Netty给channel分配NIO EventLoop的原则是什么

通过下图里可以看出EventLoopGroup其实就是一个EventExecutorGroup
在这里插入图片描述
回到ServerBootstrapServerBootstrapchildGroup本质上是一个EventLoopGroup,那么他是如何使用 childGroup.register(child)完成将某个channel与一个EventExecutor之间的绑定起来处理channel的所有任务的呢?
在这里插入图片描述
跟进register()方法的源码,选择实现类MultithreadEventLoopGroup
在这里插入图片描述
上一步会跳转到如下界面
在这里插入图片描述
继续跟进next()方法
在这里插入图片描述
继续跟进super.next(),发现其使用了一个选择器choose来实现
在这里插入图片描述
继续跟进源码,发现其有两个实现类PowerOfTwoEventExecutorChooserGenericEventExecutorChooser
在这里插入图片描述
再次进入其中某一个实现类发现上面两个实现类都是DefaultEventExecutorChooserFactory中的内部类。
在这里插入图片描述
他们选择使用哪一个内部类作为选择器的方法如下所示:具体选择方式请看代码中的注释
在这里插入图片描述
选择器选择使用哪一个EventExcutor去处理channel中的事件的方式如下:
下面中两个next()方法就是两个内部类选择的方式的源码,详情见注释。
在这里插入图片描述
总结EventLoopGroup选择某一个EventExecutor去与channel绑定去处理channel中的事件的方式就是hash的方式。只是做出了优化,因为&运算比%运算效率高,如果EventExecutor[] executors这个处理器的数组的长度为2的幂次方,那么可以使用idx.getAndIncrement() & executors.length - 1的方式实现,否则使用%运算实现。

5.通用模式的NIO实现多路复用器是如何实现跨平台的

查看NioEventLoopGroup的构造函数,跟进代码SelectorProvider.provider()
在这里插入图片描述
发现三个方法,可以获取SelectorProvider
1.loadProviderFromProperty方法
2.loadProviderAsService方法
3.sun.nio.ch.DefaultSelectorProvider.create()方法
在这里插入图片描述

5.1 跟进loadProviderFromProperty方法

发现是反射的方式去获取SelectorProvider
在这里插入图片描述

5.2 跟进loadProviderAsService方法

使用的是SPI的机制实例化对象
在这里插入图片描述

5.3 跟进sun.nio.ch.DefaultSelectorProvider.create()方法

发现里面会new WindowsSelectorProvider()来创建一个windows平台下的SelectorProvider
在这里插入图片描述
总结: 跨平台的实现是通过sun.nio.ch.DefaultSelectorProvider.create()方法实现的,不同平台下,new出来的SelectorProvider对象不同。

发布了275 篇原创文章 · 获赞 42 · 访问量 7万+

猜你喜欢

转载自blog.csdn.net/qq_35688140/article/details/105436888