Netty架构 - NioServerSocketChannel
前言
在解析Netty中的Channel如何应用之前,首先看下如下的代码:
这段代码,一些使用过netty的朋友们,都蛮了解的。
接下来从server.channel(NioServerSocketChannel.class)
这行代码开始本篇文章的解析旅程。
服务端Channel的实例化
概述:
设置非阻塞模式,建立NioServerSocketChannel与ServerSocket的关联关系,然后通过ReflectiveChannelFactory完成NioServerSocketChannel的实例化。
首先看下外层的channelFactory(…)方法做了什么事。
很显然是设置ChannelFactory属性为一个ReflectiveChannelFactory实例。
然后看下ReflectiveChannelFactory实例如何构造的。
由前文可知,方法的参数,我们传递的是NioServerSocketChannel.class
。这里利用反射调用它的无参构造器进行实例化。
先看下newSocket(…)方法的处理逻辑。
这里的SelectorProvider,通过在ide工具中debug,我们可以知道实际上是KQueueSelectorProvider
。(由SelectorProvider.provider()方法生成)
可以看出得到的是ServerSocketChannelImpl这个实例,并且将KQueueSelectorProvider
作为参数传入。
回到它的构造器方法。
- javaChannel():实际上是我们前面看到的ServerSocketChannelImpl。
- socket():构造一个ServerSocket实例。
由此,NioServerSocketChannelConfig将NioServerSocketChannel、ServerSocket与之关联。如同它的名字一样,存储相关的配置。
接着看调用其父类的构造器做了什么事。
由ch.configureBlocking(false)
这行代码,可以看出设置非阻塞模式。
接着往下看。
对parent、id、unsafe、pipeline属性进行赋值。
在AbstractBootstrap#initAndRegister()
方法有这样一行代码。
由此,由ReflectiveChannelFactory完成NioServerSocketChannel的实例化。
服务端Channel的初始化
这部分从前言例子的server.bind(port)
这行代码开始分析。
概述:
将ServerBoostrap#option(…)、attr(…)等方法设置的属性传递给NioServerSocketChannel。然后在DefaultChannelPipeline中添加ServerBootstrap#handler(…)方法定义的ChannelHandler以及ServerBootstrapAcceptor。
bind(…)方法会走到doBind(…)方法,如下:
重点关注下initAndRegister()
方法。(剩余的方法内容会在注册中接着分析)
- channelFactory:ReflectiveChannelFactory类型。
在ServerBootstrap#option(…)方法设置的参数传递给NioServerSocketChannelConfig,也就是NioServerSocketChannel的属性。
在ServerBootstrap#attr(AttributeKey, Object)方法设置的key、value传递给NioServerSocketChannel。
接着看init(…)方法。
在ServerBootstrap#childGroup(…)、childHandler(…)、childOption(…)以及childAttr(…)方法设置的参数传递给ServerBootstrapAcceptor。
在DefaultChannelPipeline中,添加了一个ChannelInitializer。其在initChannel(…)方法,定义了在DefaultChannelPipeline中添加ServerBootstrap#handler(…)方法定义的ChannelHandler、以及ServerBootstrapAcceptor实例。
ChannelInitializer,一个特定的ChannelInboundHandler,提供了便捷的方法(也就是initChannel(…)方法)用于初始化一个Channel。
ServerBootstrapAcceptor,同样是一个ChannelInboundHandler。其封装了如下属性:
服务端Channel的注册
概述:
核心是调用nio原生的ServerSocketChannelImpl#register(Selector sel, int ops, Object att)方法,将Netty的NioServerSocketChannel作为attr参数进行传入,从而与nio原生的ServerSocketChannelImpl建立关联性,最后完成Channel在Selector上的注册。
接着AbstractBootstrap#initAndRegister()方法,展开分析,有如下一行代码:
- group():ServerBootstrap#group(…)方法指定的NioEventLoopGroup。
首先看下next()方法,如下:
这里是调用父类的next()方法,然后进行类型转换。EventLoop是一个ScheduledExecutorService。
- chooser:PowerOfTwoEventExecutorChooser类型。
- executors:实际上是NioEventLoop数组。
现在,回过头看下register(…)的处理逻辑。
- channel:NioServerSocketChannel。
- this:指代NioEventLoop。
从register(…)方法中,挑选了如下代码,也就是注册Channel的核心代码。
接着跟进代码(有省略)。
- javaChannel():nio原生的ServerSocketChannelImpl。
- unwrappedSelector:KQueueSelectorImpl。
- this:指代NioServerSocketChannel。
可以看出实质上是nio原生方法 - register(Selector sel, int ops, Object att)
。将ServerSocketChannelImpl注册到Selector上,同时将Netty的NioServerSocketChannel与之关联。
深入nio的AbstractSelectableChannel#register(…)方法,一看究竟。
不难看出找到Selector关联的SelectionKey,然后通过SelectionKey#attach(…)方法将NioServerSocketChannel传入,从而建立关联性。