从Java NIO到Netty(十)

526 阅读2分钟

Netty 源码解读

新连接接入

我们继续接着上一章深入了解一下,新连接接入的源码。 见io.netty.channel.nio.AbstractNioMessageChannel.NioMessageUnsafe#read

public void read() {
            ...
            try {
                try {
                    do {
                        //1、读出SocketChannel放入List<Object> readBuf中
                        int localRead = doReadMessages(readBuf);
                        ...
                    } while (allocHandle.continueReading());
                } catch (Throwable t) {
                    exception = t;
                }

                int size = readBuf.size();
                for (int i = 0; i < size; i ++) {
                    readPending = false;
                    //2、通过pipeline处理SocketChannel
                    pipeline.fireChannelRead(readBuf.get(i));
                }
                ...
    }

1 doReadMessages(readBuf)

见io.netty.channel.socket.nio.NioServerSocketChannel#doReadMessages

 @Override
    protected int doReadMessages(List<Object> buf) throws Exception {
        //这个地方是不是非常熟悉?又是调用底层java nio通过accept创建SocketChannel
        SocketChannel ch = javaChannel().accept();

        try {
            if (ch != null) {
                //new 一个netty自己的NioSocketChannel
                //对比《从Java NIO到Netty(八)》创建NioServerSocketChannel看
                //可以看看有哪些不同点,尤其是成员变量readInterestOp 
                buf.add(new NioSocketChannel(this, ch));
                return 1;
            }
        } catch (Throwable t) {
            ...
        }

        return 0;
    }

2 pipeline.fireChannelRead(readBuf.get(i));

这个地方我调过一些中间步骤,建议大家debug跟进不然很难找到。 最终见io.netty.bootstrap.ServerBootstrap.ServerBootstrapAcceptor#channelRead

public void channelRead(ChannelHandlerContext ctx, Object msg) {
            ...
            try {
                //复习一下《从Java NIO到Netty(八)》中NioServerSocketChannel是如何注册的
                //对比看,这个childGroup就是workerGroup
                //就是把我们刚创建的NioSocketChannel注册到这个workerGroup中的一个eventLoop上面去
                //我们跟进这里看看
                childGroup.register(child).addListener(new ChannelFutureListener() {
                    @Override
                    public void operationComplete(ChannelFuture future) throws Exception {
                        if (!future.isSuccess()) {
                            forceClose(child, future.cause());
                        }
                    }
                });
            } catch (Throwable t) {
                forceClose(child, t);
            }
        }

childGroup.register(child)的内部源码已经在《从Java NIO到Netty(八)》的1.2注册selector中讲过,大家可以回去再复习一下,现在说一下区别,见io.netty.channel.AbstractChannel.AbstractUnsafe#register0

private void register0(ChannelPromise promise) {
            try {
                ...
                //区别在这里,这个isActive()是判断socket是否绑定端口,
                //在NioServerSocketChannel注册时,这个地方为false并不会执行,
                //但是NioSocketChannel注册时会执行
                if (isActive()) {
                    if (firstRegistration) {
                        //NioServerSocketChannel是在端口绑定完之后执行fireChannelActive的
                        //所以会执行这里,跟进这里
                        pipeline.fireChannelActive();
                    } 
                    ...
        }

pipeline.fireChannelActive()的执行逻辑也已经在《从Java NIO到Netty(八)》的1.2.2中讲过,大家可以再回顾一下,区别在于

@Override
    protected void doBeginRead() throws Exception {
        ...
        if ((interestOps & readInterestOp) == 0) {
            //NioServerSocketChannel把SelectionKey.OP_ACCEPT事件赋值给了这个readInterestOp
            //而NioSocketChannel把SelectionKey.OP_READ事件赋值给了这个readInterestOp
            selectionKey.interestOps(interestOps | readInterestOp);
        }
    }

那我们总结一下,新连接接入干了什么事情,是不是和java nio一样?让我们回想一下《从Java NIO到Netty(四)》这块代码

ServerSocketChannel ServerSocketChannel = (ServerSocketChannel)key.channel();
//创建SocketChannel
SocketChannel clientChannel = ServerSocketChannel.accept();
//设置为非阻塞式
clientChannel.configureBlocking(false);
//注册到selector
clientChannel.register(key.selector(), SelectionKey.OP_READ);

是不是如出一辙呢?