一、前言

上一章节中我们介绍了服务端的启动流程,并且服务端接收到新连接后,最终提交【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方法的步骤之前已有介绍,这里不赘述。直接来看大致流程图,如下
clipboard.png

可以看到,在runAllTasks方法中来执行任务【NioSocketChannel注册】,这里跟上面NioServerSocketChannel的注册调用的是同一个方法代码,大致流程其实和【NioServerSocketChannel注册】是一样的,但是有一个地方是不同的,就是NioSocketChannel的激活之前NioServerSocketChannel的激活是单独提交一个任务)。这里直接调用DefaultChannelPipeline的fireChannelActive方法,在HeadContext的channelActive方法中,内部调用readIfIsAutoRead()方法来设置SelectionKey的监听事件,NioSocketChannel为READ事件

【NioSocketChannel注册】任务执行完成之后,这时该workerGroup的NioEventLoop调用Selector的select方法进行阻塞,并监听该NioSocketChannel的READ事件。

三、读取客户端发送的数据

当接收到客户端发送过来的数据时,该workerGroup的NioEventLoop被唤醒,来看下NioEventLoop唤醒之后是如何读取数据的?
clipboard.png

如上可以看到,当被唤醒之后会调用NioByteUnsafe的read方法,内部包括4个步骤:

  1. 分配ByteBuf
  2. 调用java nio的SocketChannel的read方法来读取数据
  3. 调用DefaultChannelPipeline的fireChannelRead方法,从head开始往后逐一调用ChannelHandler的channelRead方法,具体步骤如下
    clipboard.png
    可以看到先调用了HeadContext的channelRead方法,紧接着执行EchoServerHandler的channelRead方法,由于其内部执行ctx.write方法,所以会直接回头调用重写了write方法的ChannelHandler,所以这里是调用HeadContext的write方法,其内部调用了ChannelOutboundBuffer的addMessage方法,将数据添加到出站消息的缓冲区中
  4. 调用DefaultChannelPipeline的fireChannelReadComplete方法,从head开始往后逐一调用ChannelHandler的channelReadComplete方法,具体步骤如下
    clipboard.png
    可以看到先调用了HeadContext的channelReadComplete方法,紧接着执行EchoServerHandler的channelReadComplete方法,由于其内部执行ctx.flush方法,所以会回头调用重写了flush方法的ChannelHandler,所以这里调用HeadContext的flush方法,其内部调用了SocketChannel的write方法,将数据写到客户端

四、小结

至此服务端读取客户端发送的数据的流程就结束了,从该源码中我们可以看到NioEventLoop在其中扮演了至关重要的角色,其ChannelPipeline的编排也是重要的拓展点,虽然这里我们只是使用EchoServerHandler来演示流程,但实际开发中我们可以通过添加各种自定义的ChannelHandler来实现不同的业务逻辑。


kamier
1.5k 声望493 粉丝