一、前言
上一章节中咱们介绍了服务端的启动流程,并且服务端接管到新连贯后,最终提交【NioSocketChannel注册】工作给workerGroup的NioEventLoop
。
因为咱们NioSocketChannel对应的ChannelPipeline增加了一个EchoServerHandler,所以此时NioSocketChannel对应的ChannelPipeline链是这样的:HeadContext -> EchoServerHandler -> TailContext
,来看下EchoServerHandler的源码
public class EchoServerHandler extends ChannelInboundHandlerAdapter { @Override public void channelRead(ChannelHandlerContext ctx, Object msg) { ctx.write(msg); } @Override public void channelReadComplete(ChannelHandlerContext ctx) { ctx.flush(); } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { // Close the connection when an exception is raised. cause.printStackTrace(); ctx.close(); }}
接下来看下workerGroup的NioEventLoop是如何执行【NioSocketChannel注册】工作的?
二、NioSocketChannel注册
这里假设该workerGroup的NioEventLoop对应的线程名为NioEventLoopGroup3-1
。此时该workerGroup的NioEventLoop接管到了第一个工作,开始进行初始化工作,启动线程并绑定,开始执行NioEventLoop类的run办法
。run办法的步骤之前已有介绍,这里不赘述。间接来看大抵流程图,如下
能够看到,在runAllTasks办法中来执行工作【NioSocketChannel注册】,这里跟下面NioServerSocketChannel的注册调用的是同一个办法代码,大抵流程其实和【NioServerSocketChannel注册】是一样的,然而有一个中央是不同的,就是NioSocketChannel的激活
(之前NioServerSocketChannel的激活是独自提交一个工作)。这里间接调用DefaultChannelPipeline的fireChannelActive办法,在HeadContext的channelActive办法中,外部调用readIfIsAutoRead()办法来设置SelectionKey的监听事件,NioSocketChannel为READ事件
。
【NioSocketChannel注册】工作执行实现之后,这时该workerGroup的NioEventLoop调用Selector的select办法进行阻塞,并监听该NioSocketChannel的READ事件。
三、读取客户端发送的数据
当接管到客户端发送过去的数据时,该workerGroup的NioEventLoop被唤醒,来看下NioEventLoop唤醒之后是如何读取数据的?
如上能够看到,当被唤醒之后会调用NioByteUnsafe的read办法,外部包含4个步骤:
- 调配ByteBuf
- 调用java nio的
SocketChannel的read办法
来读取数据 - 调用DefaultChannelPipeline的fireChannelRead办法,从head开始往后逐个调用ChannelHandler的channelRead办法,具体步骤如下
能够看到先调用了HeadContext的channelRead办法,紧接着执行EchoServerHandler的channelRead办法,因为其外部执行ctx.write办法,所以会间接回头调用
重写了write办法的ChannelHandler,所以这里是调用HeadContext的write办法,其外部调用了ChannelOutboundBuffer的addMessage办法,将数据增加到出站音讯的缓冲区中
。 - 调用DefaultChannelPipeline的fireChannelReadComplete办法,从head开始往后逐个调用ChannelHandler的channelReadComplete办法,具体步骤如下
能够看到先调用了HeadContext的channelReadComplete办法,紧接着执行EchoServerHandler的channelReadComplete办法,因为其外部执行ctx.flush办法,所以会回头调用重写了flush办法的ChannelHandler,所以这里调用HeadContext的flush办法,其外部调用了SocketChannel的write办法,将数据写到客户端
。
四、小结
至此服务端读取客户端发送的数据的流程就完结了,从该源码中咱们能够看到NioEventLoop在其中表演了至关重要的角色,其ChannelPipeline的编排也是重要的拓展点,尽管这里咱们只是应用EchoServerHandler来演示流程,但理论开发中咱们能够通过增加各种自定义的ChannelHandler来实现不同的业务逻辑。