initAndRegister方法
主要有三个方法
channelFactory.newChannel方法
1)初始化NioServerSocketChannel,设置为非阻塞状态,保存了感兴趣的事件类型为accept;
2)创建服务端Channel的pipeline,但管道中只有HeadContext<——>TailContext;
3)创建服务端Channel的Unsafe对象,类型是NioMessageUnsafe对象;
init方法
向服务端pipeline中添加一个ChannelInitializer实例,并且加到另外一个链表中,为服务端channel向selector注册之后的回调(拆包,异步任务)做准备。
1)初始化服务端pipeline,向服务端管道中添加一个ChannelInitializer实例,形成HeadContext<——>ChannelInitializer<——>TailContext;另外addLast方法中还会将ChanneInitializer实例加入到PendingHandlerCallback链表中构成一个单向链表,当服务端Channel注册之后,会从该链表中取出CI实例执行initChannel方法。
2)ChannelInitializer实例中只有一个initChannel方法中,其包括两部分逻辑,即往服务端pipeline添加handler属性中保存的处理器和往bossGroup的nioEventLoop中提交异步任务2;调用initChannel方法的时机是服务端channel注册,走到AbstractChannel#register0方法,其第一步执行完服务端channel向selector注册之后,紧接着会执行invokeHandlerAddedIfNeeded方法,其中会从PendingHandlerCallback链表中拿到ChannelInitializer实例执行其中的initChannel方法,第一步添加保存在handler属性中的处理器,第二步向服务端Channel的NioEventLoop的任务队列中提交一个异步任务2,任务内容为pipeline.addLast(new ServerBootstrapAcceptor(xxx)),ServerBootstrapAcceptor 继承了ChannelInboundHandlerAdapter,当后续建立客户端channel时,会调用到serverBootstrapAcceptor#channelRead方法,将属性childHandler中保存的处理器添加至客户端pipeline中,再执行AbstractChannel#register0方法;
执行bossGroup的register方法
服务端channel注册到selector时调用的是AbstractBootstrap#initAndRegister中的group().register方法,客户端channel注册到selector时调用的是ServerBootstrapAcceptor#channelRead中的childGroup.register方法,group和childGroup分别代表bossGroup和workerGroup,但其实两者本质相同都是nioEventLoopGroup,所以两者最终都会调用nioEventLoopGroup父类MultithreadEventLoopGroup的register方法,主要区别在于selector上绑定的channel类型不同,监听的事件类型不同。
next方法从bossGroup线程组中拿到一个线程NioEventLoop实例来处理服务端channel的注册,将服务端channel注册到线程自己的selector上,NioEventLoop可以看做是单线程线程池,一是处理需要注册到selector上的io连接事件,二是需要处理任务队列中的任务;
1)服务端Channel封装成ChannelPromise,调用NioMessageUnsafe#register方法,最终调用父类AbstractNioUnsafe#register方法,传入当前NioEventLoop实例和ChannelPromise;
2)当前线程是否在运行状态,若不是,则将register0方法交给当前NioEventLoop实例作为异步任务1运行;若是则直接由当前线程执行register0方法;最后返回ChannelFuture实例。判断是否在运行状态的条件是当前线程和singleThreadEventExecutor中的thread是否为同一个线程,若是则表示当前线程正在运行,因为nioEventLoop中线程的创建是在提交任务的时候创建的,不相等,基本上是为null,即还未创建线程,所以暂时提交到nioEventLoop的LinkedBlockingQueue类型的任务队列中;
3)提交register0异步任务至NioEventLoop的任务队列后,程序紧接着会给ThreadPerTaskExecutor线程池提交一个任务以启动当前线程,任务主要内容是SingleThreadEventExecutor.this.run方法,线程池在收到任务后,会新建一个线程,去执行该任务,该任务中会执行SingleThreadEventExecutor.this.run方法,该方法是个自旋,主要处理channel事件相关和任务队列中的任务;
register0方法
ThreadPerTaskExecutor收到一个任务后,会启动一个线程,进入自旋,开始执行任务队列中的异步任务,并且监听channel上的各种事件;
1)register0中首先会将当前服务端channel注册到boss线程组的NioEventLoop实例中的selector上;
2)接着会执行pipeline.invokeHandlerAddedIfNeeded()方法,找到PendingHandlerCallback单向链表,取出在ServerBootstrap#init方法中添加的ChannelInitializer实例,执行其中的initChannel方法,该方法中包括两个逻辑,向服务端pipeline中添加config中保存的handler和提交异步任务2到NioEventLoop的任务队列,最后将ChannelInitializer从pipeline中删除;异步任务2的具体逻辑是向服务端pipeline中添加ServerBootstrapAcceptor实例,与客户端pipeline的初始化相关;
3)执行safeSetSuccess方法,设置ChannelPromise为成功,此时initAndRegister方法执行完毕,会回到doBind中执行回调,即doBind0方法,会提交异步任务3;
4)执行pipeline的fireChannelRegistered方法,执行管道中每个handler的channelRegistered方法。
doBind0
此方法是紧接在initAndRegister方法之后,通过检查ChannelFuture实例返回值,若register0方法执行完毕,则执行doBind0方法,否则,给register0方法所在的ChannelFuture实例注册一个回调,当register0方法执行完后,再执行doBind0方法;最后又返回doBind0方法相关的Promise实例,用于监听doBind0方法是否执行完毕。主线程会阻塞,直到doBind0执行完毕。
向NioEventLoop的任务队列提交了一个异步任务3
1)当前服务端channel绑定地址,端口号;
2)向当前NioEventLoop实例的任务队列中提交异步任务4,代码追踪AbstractBootstrap#channel.bind方法——>AbstractChannelHandlerContext#bind方法中执行next.invokeBind方法——>HeadContext#bind方法——>AbstractUnsafe#bind方法,在该方法中分别执行doBind方法,和提交异步任务4等,该任务4是执行fireChannelActive方法,这是个inBound类型的事件,首选找到HeadContext#ChannelActive方法,TailContext中该方法为空实现,接着走readIfIsAutoRead方法,再次向pipeline中发起read事件,最终目的是设置服务端channel感兴趣的事件为accept(以前只是保存),此时代表服务端channel被激活;
3)设置ChannelPromise为success,唤醒阻塞在sync方法上的主线程;
异步任务之间的关系
register方法中提交异步任务1:register0;
异步任务1:
1)先完成注册;
2)回调CI中的initChannel方法拆包和提交异步任务2:向服务端pipeline中添加一个ServerBootstrapAcceptor实例;
3)执行safeSetSuccess方法,设置ChannelPromise为成功,执行AbstractBootStrap#doBind中的回调方法,提交异步任务3:AbstractChannel#bind方法;
4)执行fireChannelRegistered;
异步任务3:
1)绑定地址端口
2)提交异步任务4:fireChannelActive;
3)执行safeSetSuccess方法,设置ChannelPromise为成功,返回ChannelPromise到AbstractBootStrap#doBind中,此时阻塞在ChannelFuture f = b.bind(PORT).sync()处的主线程会被唤醒。
异步任务1中提交异步任务2和异步任务3到当前NioEventLoop任务队列,异步任务3中提交异步任务4到当前NioEventLoop任务队列;
主线程会在两个方法上阻塞
ChannelFuture f = b.bind(PORT).sync();异步任务4执行成功后,会唤醒主线程
f.channel().closeFuture().sync();服务端channel关闭后,会唤醒主线程