上一章 中的标准netty启动代码中,ServerBootstrap到底是如何启动的呢?这一章我们来瞅下。
server.group(bossGroup, workGroup);
server.channel(NioServerSocketChannel.class).option(ChannelOption.SO_BACKLOG, 100);
server.childHandler(new DealNettyServerInitializer());
ChannelFuture future = server.bind(7878).sync();
启动代码无非这么几行,我一行一行的瞅。
server.group(bossGroup, workGroup);
还记得上一章定义的两个NioEventLoopGroup不?bossGroup和workGroup。这里的ServerBootstrap的group方法就是将workGroup直接赋值给了该对象的childGroup。而bossGroup传到了其父类AbstractBootstrap中,进行保存。
看到这,我们知道了两个类的关系:ServerBootstrap是AbstractBootstrap的子类。
server.channel(NioServerSocketChannel.class).option(ChannelOption.SO_BACKLOG, 100);
会将NioServerSocketChannel的class对象作为参数构建ReflectiveChannelFactory对象。这是什么对象呢?进去看一下:
哦,原来是一个通过反射构建对象的工厂类。回到上一步,netty会把ReflectiveChannelFactory对象作为参数调用channelFactory()方法。进入这个方法看一下:
原来只是保存ReflectiveChannelFactory对象。注意,channel方法是属于AbstractBootstrap对象的,因此,ReflectiveChannelFactory对象是保存在AbstractBootstrap对象中的。
同样,option方法也是AbstractBootstrap对象的,options会保存该option对象,那这么说,options应该是一个集合了,
果然是一个map集合。
server.childHandler(new DealNettyServerInitializer());
childHandler是属于ServerBootstrap类的,所以将DealNettyServerInitializer对象赋值给了ServerBootstrap的childHandler成员变量。
至此,只是保存变量,没有使用。看下server.bind(7878)方法,此方法就是服务器启动的真正入口。
很简单,继续跟进,
调用doBind方法,继续跟进:
这么多代码,主要是initAndRegister方法,因此跟进去。
下面正式进入启动流程,启动流程大体分为4步:
1、创建服务端channel:NioServerSocketChannel
2、初始化服务端Channel:NioServerSocketChannel
3、注册Selector:将Channel注册到Selector上
4、端口的绑定:服务端端口的监听。
下面,一步一步的分析:
1、创建服务端channel:NioServerSocketChannel
忘了说了,这个bind方法是调用的AbstractBootstrap的方法哦,因此这个channelFactory就是channel()方法的参数ReflectiveChannelFactory,下图为证:
还记的ReflectiveChannelFactory的newChannel方法吗?
很明显,就是通过反射构建NioServerSocketChannel。因为我们传入的是NioServerSocketChannel的Class对象。
NioServerSocketChannel对象是生成了,我们一起看一下这个NioServerSocketChannel的构造方法吧。
这个newSocket是做了什么?
哦,原来是调用jdk的代码,生成Nio的ServerSocketChannel呀。继续查看:
跟进this:
记住,这里的第三个参数是accept事件,,后续我们还会提到。跟进父类的构造方法:
我们可以看到,NioServerSocketChannel的父类是AbstractNioMessageChannel。然后这个构造方法只是继续调用父类的构造方法。
我们又知道了,AbstractNioMessageChannel的父类是AbstractNioChannel。并且刚才说注意的accept事件,赋值给了AbstractNioChannel的成员变量readInterestOp,后续还会遇到。
注意这个方法,ch是什么?就是传进来的jdk原生的ServerSocketChannel,一次该方法明显是将此通道将被置于非阻塞模式。
继续跟进父类的构造方法:
我们又知道了AbstractNioChannel的父类是AbstractChannel。并且,在该构造方法中,实例化了id,unsafe和pipeline。
明显此处的NioMessageUnsafe的父类是AbstracNioUnsafe。
到这里,一系列的父类构造方法的调用结束了。我们先总结下他们之间的关系。
NioServerSocketChannel的父类是AbstractNioMessageChannel;AbstractNioMessageChannel的父类是AbstractNioChannel;AbstractNioChannel的父类是AbstractChannel;还有一个额外的关系
NioMessageUnsafe的父类是AbstractNioUnsafe。
即:
NioServerSocketChannel -> AbstractNioMessageChannel -> AbstractNioChannel -> AbstractChannel;
NioMessageUnsafe -> AbstractNioUnsafe
为啥非要强调这种继承关系?因为后续的调用很复杂,如果不记住他们的关系,很容易分不清楚变量是从何 而来的。。
好了,我再回到开始的NioServerSocketChannel的构造方法上:
看过了super一系列的调用,我们瞅下下面的代码。先看下javaChannel()干了什么?
调用了父类的javaChannel方法。NioServerSocketChannel的父类是AbstractNioMessageChannel,因此看AbstractMessageNioChannel类的javaChannel方法,我们发现AbstractMessageNioChannel类中并没有javaChannel方法,因此应该调用的是AbstractNioMessageChannel的父类AbstractNioChannel的javaChannel,我们看一下:
这个ch,不就是之前传入的jdk原声的ServerSocketChannel对象么。。。图下证:
因此javaChannel方法,获取的是原生的ServerSocketChannel对象。点个赞,这个方法的名字真是见名知意呀。那javaChannel().socket()我们就很容易知道了,就是打开一个socket连接。并将该连接封装到NioServerSocketChannelConfig对象中保存在NioServerSocketChannel的config成员变量上。至此,创建服务端channel:NioServerSocketChannel完成。
2、初始化服务端Channel:NioServerSocketChannel
回到initAndRegister方法,看完了NioServerSocketChannel的实例化,我们看下紧接着的代码init():
进入init方法:
哎呦,这个又一坨代码。。。。没关系,很多代码都很简单:
上面代码很简单,就是setChannelOptions和setChannelAttrs。
同理,setChildOptions和setChildAttrs。
首先声明下,initChannel方法不是立即被调用的哦,后面会讲到何时被调用。总之会被调用,但不是这里。
p是啥,
,这个p就是NioServerSocketChannel里成员变量,准确的说是父类父类父类的成员变量,即AbstractChannel的成员变量。下图证:
那个initChannel的方法里的pipeline变量也是p。那pipeline是啥呢?以后会讲的,这里只要知道它是一个双向链表结构就可以了。
因此这段的意思是将handler对象放入到pipeline中。
看到这里,一下懵了,NioServerSocketChannel里面啥时候实例化的EventLoop对象呀?说明下,此刻还没有实例化,因为没有调用该方法,所以暂时不会又null异常。
这段的逻辑就是向pipeline中添加连接器ServerBootstrapAcceptor。
好了,今天先到这,下面两步代码太深,明天再继续吧。最后,一定要记住继承的关系。