Netty源碼分析(三)--- 新連接進來如何初始化NioSocketChannel

一、NioSocketChannel的初始化

  1. 從NioEventLoop開始說起
    我們在服務端啓動後,boss線程組會啓動一個NioEventLoop線程,它會在run方法中無限循環接收感興趣的事件
    //1. NioEventLoop
    //新連接進來會觸發ACCEPT事件
    @Override
    protected void run() {
        for (;;) {
            try {
                switch (selectStrategy.calculateStrategy(selectNowSupplier, hasTasks())) {
                    case SelectStrategy.CONTINUE:
                        continue;
                    case SelectStrategy.SELECT:
                        select(wakenUp.getAndSet(false));
    
                        if (wakenUp.get()) {
                            selector.wakeup();
                        }
                        // fall through
                    default:
                }
    
                cancelledKeys = 0;
                needsToSelectAgain = false;
                final int ioRatio = this.ioRatio;
                if (ioRatio == 100) {
                    try {
                        processSelectedKeys();
                    } finally {
                        // Ensure we always run tasks.
                        runAllTasks();
                    }
                } else {
                    final long ioStartTime = System.nanoTime();
                    try {
                    	//處理selectkey
                    	//1 - (1)
                        processSelectedKeys();
                    } finally {
                        // Ensure we always run tasks.
                        final long ioTime = System.nanoTime() - ioStartTime;
                        runAllTasks(ioTime * (100 - ioRatio) / ioRatio);
                    }
                }
            } catch (Throwable t) {
                handleLoopException(t);
            }
            // Always handle shutdown even if the loop processing threw an exception.
            try {
                if (isShuttingDown()) {
                    closeAll();
                    if (confirmShutdown()) {
                        return;
                    }
                }
            } catch (Throwable t) {
                handleLoopException(t);
            }
        }
    }
    
    //1 - (1)
    private void processSelectedKeys() {
        if (selectedKeys != null) {
        	//1 - (2)
            processSelectedKeysOptimized();
        } else {
            processSelectedKeysPlain(selector.selectedKeys());
        }
    }
    
    //1 - (3)
    private void processSelectedKeysOptimized() {
        for (int i = 0; i < selectedKeys.size; ++i) {
            final SelectionKey k = selectedKeys.keys[i];
            // null out entry in the array to allow to have it GC'ed once the Channel close
            // See https://github.com/netty/netty/issues/2363
            selectedKeys.keys[i] = null;
            
    		//拿到selector時候netty設置然後jdk保存的NioServerSocketChannel
            final Object a = k.attachment();
    
            if (a instanceof AbstractNioChannel) {
            	//1 - (4)
                processSelectedKey(k, (AbstractNioChannel) a);
            } else {
                @SuppressWarnings("unchecked")
                NioTask<SelectableChannel> task = (NioTask<SelectableChannel>) a;
                processSelectedKey(k, task);
            }
    
            if (needsToSelectAgain) {
                // null out entries in the array to allow to have it GC'ed once the Channel close
                // See https://github.com/netty/netty/issues/2363
                selectedKeys.reset(i + 1);
    
                selectAgain();
                i = -1;
            }
        }
    }
    
    //1 - (4)   這裏就類似java的Nio處理不同的感興趣事件
    private void processSelectedKey(SelectionKey k, AbstractNioChannel ch) {
        final AbstractNioChannel.NioUnsafe unsafe = ch.unsafe();
        if (!k.isValid()) {
            final EventLoop eventLoop;
            try {
                eventLoop = ch.eventLoop();
            } catch (Throwable ignored) {
                // If the channel implementation throws an exception because there is no event loop, we ignore this
                // because we are only trying to determine if ch is registered to this event loop and thus has authority
                // to close ch.
                return;
            }
            // Only close ch if ch is still registered to this EventLoop. ch could have deregistered from the event loop
            // and thus the SelectionKey could be cancelled as part of the deregistration process, but the channel is
            // still healthy and should not be closed.
            // See https://github.com/netty/netty/issues/5125
            if (eventLoop != this || eventLoop == null) {
                return;
            }
            // close the channel if the key is not valid anymore
            unsafe.close(unsafe.voidPromise());
            return;
        }
    
        try {
            int readyOps = k.readyOps();
            // We first need to call finishConnect() before try to trigger a read(...) or write(...) as otherwise
            // the NIO JDK channel implementation may throw a NotYetConnectedException.
            if ((readyOps & SelectionKey.OP_CONNECT) != 0) {
                // remove OP_CONNECT as otherwise Selector.select(..) will always return without blocking
                // See https://github.com/netty/netty/issues/924
                int ops = k.interestOps();
                ops &= ~SelectionKey.OP_CONNECT;
                k.interestOps(ops);
    
                unsafe.finishConnect();
            }
    
            // Process OP_WRITE first as we may be able to write some queued buffers and so free memory.
            if ((readyOps & SelectionKey.OP_WRITE) != 0) {
                // Call forceFlush which will also take care of clear the OP_WRITE once there is nothing left to write
                ch.unsafe().forceFlush();
            }
    
            // Also check for readOps of 0 to workaround possible JDK bug which may otherwise lead
            // to a spin loop
            if ((readyOps & (SelectionKey.OP_READ | SelectionKey.OP_ACCEPT)) != 0 || readyOps == 0) {
    			//會進入到NioMessageUnsafe的read方法
    			//1 - (5)
                unsafe.read();
            }
        } catch (CancelledKeyException ignored) {
            unsafe.close(unsafe.voidPromise());
        }
    }
    
    
    //1 - (5) NioMessageUnsafe
    @Override
    public void read() {
    	//是否在當前EventLoop線程
        assert eventLoop().inEventLoop();
        //NioServerSocketChannel的config
        final ChannelConfig config = config();
        //NioServerSocketChannel的通道
        final ChannelPipeline pipeline = pipeline();
        //分配一個接收緩衝區,二分算法獲取一個不會浪費空間又又足夠大小緩衝區
        final RecvByteBufAllocator.Handle allocHandle = unsafe().recvBufAllocHandle();
        //重置計數器
        allocHandle.reset(config);
    
        boolean closed = false;
        Throwable exception = null;
        try {
            try {
                do {
                	//1 - (6)
                    int localRead = doReadMessages(readBuf);
                    if (localRead == 0) {
                        break;
                    }
                    if (localRead < 0) {
                        closed = true;
                        break;
                    }
    				//總消息數加一
                    allocHandle.incMessagesRead(localRead);
                 //是否繼續讀消息
                 //1 - (11)
                } while (allocHandle.continueReading());
            } catch (Throwable t) {
                exception = t;
            }
    
            int size = readBuf.size();
            for (int i = 0; i < size; i ++) {
                readPending = false;
                //關鍵地方來了,NioSocketChannel的註冊和綁定到selector上
                //調用NioServerSocketChannel的pipeline中ChannelHandler的read
                //1 - (12)
                pipeline.fireChannelRead(readBuf.get(i));
            }
            readBuf.clear();
            allocHandle.readComplete();
            //read完成事件
            pipeline.fireChannelReadComplete();
    
            if (exception != null) {
                closed = closeOnReadError(exception);
    
                pipeline.fireExceptionCaught(exception);
            }
    
            if (closed) {
                inputShutdown = true;
                if (isOpen()) {
                    close(voidPromise());
                }
            }
        } finally {
            // Check if there is a readPending which was not processed yet.
            // This could be for two reasons:
            // * The user called Channel.read() or ChannelHandlerContext.read() in channelRead(...) method
            // * The user called Channel.read() or ChannelHandlerContext.read() in channelReadComplete(...) method
            //
            // See https://github.com/netty/netty/issues/2254
            if (!readPending && !config.isAutoRead()) {
                removeReadOp();
            }
        }
    }
    
    //1 - (6) NioServerSocketChannel
    @Override
    protected int doReadMessages(List<Object> buf) throws Exception {
    	//獲取Java的SocketChannel
        SocketChannel ch = SocketUtils.accept(javaChannel());
    
        try {
            if (ch != null) {
            	//包裝成netty的的NioSocketChannel並加進NioMessageUnsafe的readBuf集合中
            	//1 - (7)
                buf.add(new NioSocketChannel(this, ch));
                return 1;
            }
        } catch (Throwable t) {
            logger.warn("Failed to create a new channel from an accepted socket.", t);
    
            try {
                ch.close();
            } catch (Throwable t2) {
                logger.warn("Failed to close a socket.", t2);
            }
        }
    
        return 0;
    }
    
    //1 - (7)  NioSocketChannel --- 初始化
    public NioSocketChannel(Channel parent, SocketChannel socket) {
    	//1 - (8)
        super(parent, socket);
        //創建並保存NioSocketChannelConfig
        config = new NioSocketChannelConfig(this, socket.socket());
    }
    
    //1 - (8) AbstractNioByteChannel
    protected AbstractNioByteChannel(Channel parent, SelectableChannel ch) {
    	//1 - (9)
        super(parent, ch, SelectionKey.OP_READ);
    }
    
    
    //1 - (9) AbstractNioChannel
    protected AbstractNioChannel(Channel parent, SelectableChannel ch, int readInterestOp) {
    	//1 - (10)
        super(parent);
        this.ch = ch;
        //保存感興趣的讀事件
        this.readInterestOp = readInterestOp;
        try {
        	//jdk的非阻塞獲取連接
            ch.configureBlocking(false);
        } catch (IOException e) {
            try {
                ch.close();
            } catch (IOException e2) {
                if (logger.isWarnEnabled()) {
                    logger.warn(
                            "Failed to close a partially initialized socket.", e2);
                }
            }
    
            throw new ChannelException("Failed to enter non-blocking mode.", e);
        }
    }
    
    //1 - (10) AbstractChannel
    protected AbstractChannel(Channel parent) {
    	//保存NioServerSocketChannel
        this.parent = parent;
        //設置一個id
        id = newId();
        //創建並保存NioSocketChannelUnsafe
        unsafe = newUnsafe();
         //創建並保存DefaultChannelPipeline
        pipeline = newChannelPipeline();
    }
    
    
    //1 - (11) 
    @Override
    public boolean continueReading() {
         return continueReading(defaultMaybeMoreSupplier);
     }
    
    @Override
     public boolean continueReading(UncheckedBooleanSupplier maybeMoreDataSupplier) {
         //config.isAutoRead()  true
         //respectMaybeMoreData   true
         //maybeMoreDataSupplier.get()  true
         //totalMessages < maxMessagePerRead   true
         //totalBytesRead > 0  false
         return config.isAutoRead() &&
                (!respectMaybeMoreData || maybeMoreDataSupplier.get()) &&
                totalMessages < maxMessagePerRead &&
                totalBytesRead > 0;
     }
    
    //1 - (12)  DefaultChannelPipeline
    @Override  
    public final ChannelPipeline fireChannelRead(Object msg) {
    	//1 - (13)
        AbstractChannelHandlerContext.invokeChannelRead(head, msg);
        return this;
    }
    
    //1 - (13) AbstractChannelHandlerContext
    static void invokeChannelRead(final AbstractChannelHandlerContext next, Object msg) {
    	//m=NioSocketChannel
        final Object m = next.pipeline.touch(ObjectUtil.checkNotNull(msg, "msg"), next);
        EventExecutor executor = next.executor();
        if (executor.inEventLoop()) {
        	//1 - (14)
            next.invokeChannelRead(m);
        } else {
            executor.execute(new Runnable() {
                @Override
                public void run() {
                    next.invokeChannelRead(m);
                }
            });
        }
    }
    
    //1 - (14) AbstractChannelHandlerContext
    private void invokeChannelRead(Object msg) {
        if (invokeHandler()) {
            try {
            	//head節點開始 → ServerBootstrapAcceptor
            	//1 - (15)
                ((ChannelInboundHandler) handler()).channelRead(this, msg);
            } catch (Throwable t) {
                notifyHandlerException(t);
            }
        } else {
            fireChannelRead(msg);
        }
    }
    
    //1 - (15)
    @Override
    @SuppressWarnings("unchecked")
    public void channelRead(ChannelHandlerContext ctx, Object msg) {
    	//child = NioSocketChannel
        final Channel child = (Channel) msg;
    	//NioSocketChannel的pipeline中把設置的ChannelHandler添加進去
        child.pipeline().addLast(childHandler);
    	//設置TCP參數
        setChannelOptions(child, childOptions, logger);
    	//設置屬性
        for (Entry<AttributeKey<?>, Object> e: childAttrs) {
            child.attr((AttributeKey<Object>) e.getKey()).set(e.getValue());
        }
    
        try {
        	//這裏就類似Server端NioServerSocketChannel的初始化
        	//從worker線程組拿一個NioEventLoop 並將NioSocketChannel註冊到NioEventLoop 上
            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);
        }
    }
    
發佈了25 篇原創文章 · 獲贊 3 · 訪問量 1511
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章