Netty源碼分析(四)--- NioEventLoop如何讀取消息

一、NioEventLoop工作過程

  1. 貼一段Netty Server端和Client端的demo

    //1. Server端
    public class NettyServerTest {
    
        public static void main(String[] args) {
            EventLoopGroup boss = new NioEventLoopGroup(1);
            EventLoopGroup worker = new NioEventLoopGroup(16);
    
            try {
                ServerBootstrap serverBootstrap = new ServerBootstrap();
                serverBootstrap.group(boss, worker)
                        .channel(NioServerSocketChannel.class)
                        .handler(new ChannelHandler() {
                            @Override
                            public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
                                System.out.println("executed method handlerAdded");
                            }
    
                            @Override
                            public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
    
                            }
    
                            @Override
                            public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
    
                            }
                        })
                        .childHandler(new ChannelInitializer<SocketChannel>() {
    
                            @Override
                            protected void initChannel(SocketChannel ch) throws Exception {
                                ChannelPipeline pipeline = ch.pipeline();
                                pipeline.addLast(new StringDecoder(CharsetUtil.UTF_8))
                                        .addLast(new StringEncoder(CharsetUtil.UTF_8))
                                        .addLast(new ChannelInBoundHandelerTest())
                                        .addLast(new ChannelOutBoundHandelerTest1())
                                        .addLast(new ChannelOutBoundHandelerTest2());
                            }
    
                        });
                ChannelFuture future = serverBootstrap.bind("127.0.0.1", 8081).sync();
                future.channel().closeFuture().sync();
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                boss.shutdownGracefully();
                worker.shutdownGracefully();
            }
        }
    }
    
    //2. server端 ChannelHandler
    //(1) ChannelInboundHandler
    public class ChannelInBoundHandelerTest extends ChannelInboundHandlerAdapter {
    
        @Override
        public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
            System.out.println(msg);
            ctx.writeAndFlush("NoDelay");
            //當前InBound開始向尾結點傳播
            ctx.fireChannelRead(msg);
        }
    
        @Override
        public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
            super.channelReadComplete(ctx);
        }
    
        @Override
        public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
            cause.printStackTrace();
            ctx.close();
        }
    }
    
    //(2)ChannelOutboundHandler
    public class ChannelOutBoundHandelerTest1 extends ChannelOutboundHandlerAdapter {
    
        @Override
        public void read(ChannelHandlerContext ctx) throws Exception {
            super.read(ctx);
    
            System.out.println("ChannelOutBoundHandelerTest1");
        }
    
        @Override
        public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
            super.exceptionCaught(ctx, cause);
        }
    }
    //(3)ChannelOutboundHandler
    public class ChannelOutBoundHandelerTest2 extends ChannelOutboundHandlerAdapter {
    
        @Override
        public void read(ChannelHandlerContext ctx) throws Exception {
            super.read(ctx);
            System.out.println("ChannelOutBoundHandelerTest2");
        }
    
        @Override
        public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
            super.exceptionCaught(ctx, cause);
        }
    }
    
    //3. Client
    public class NettyClientTest {
    
    public static void main(String[] args) {
            NioEventLoopGroup group = new NioEventLoopGroup(1);
    
            try {
                Bootstrap bootstrap = new Bootstrap();
                bootstrap.group(group)
                        .channel(NioSocketChannel.class)
                        .handler(new ChannelInitializer<SocketChannel>() {
                            @Override
                            protected void initChannel(SocketChannel ch) throws Exception {
                                ChannelPipeline pipeline = ch.pipeline();
                                pipeline.addLast(new StringDecoder(CharsetUtil.UTF_8))
                                        .addLast(new StringEncoder(CharsetUtil.UTF_8))
                                        .addLast(new ClientChannelOutBoundHandelerTest());
                            }
                        });
                ChannelFuture future = bootstrap.connect("127.0.0.1", 8081).sync();
                future.channel().closeFuture().sync();
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                group.shutdownGracefully();
            }
        }
    }
    
    //4. Client
    //(1)ChannelInboundHandler
    public class ClientChannelOutBoundHandelerTest extends ChannelInboundHandlerAdapter {
    
        @Override
        public void channelActive(ChannelHandlerContext ctx) throws Exception {
            ctx.writeAndFlush("Send message");
        }
    
        @Override
        public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
            System.out.println(msg);
        }
    
        @Override
        public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
            super.channelReadComplete(ctx);
        }
    
        @Override
        public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
            super.exceptionCaught(ctx, cause);
        }
    }
    

    啓動服務端和客戶端
    服務端結果
    在這裏插入圖片描述
    客戶端結果
    在這裏插入圖片描述
    ps:服務端多執行一次OutBound的1和2是因爲NioServerSocketChannel所在的NioEventLoop建立連接執行感興趣事件ACCPET時,創建NioSocketChannel並進行註冊,會調用AbstractChannel中register0方法的pipeline.fireChannelActive();從尾部節點開始調用OutBound~~~

  2. NioEventLoop主要用於處理IO操作 + taskQueue中的任務 (也就是負責 IO線程 + 任務線程)
    註冊時候會將NioEventLoop的線程啓動,會調用它的run方法。

    //1. NioEventLoop
    @Override
    protected void run() {
        for (;;) {
            try {
            	//hasTasks() → 任務隊列和尾部隊列是否有任務
            	//有任務的話立即執行,並喚醒selector
                switch (selectStrategy.calculateStrategy(selectNowSupplier, hasTasks())) {
                	//默認不會有這個策略(沒看到返回這個策略的)
                    case SelectStrategy.CONTINUE:
                        continue;
                    //阻塞去select
                    case SelectStrategy.SELECT:
                    	//這裏解決了java臭名昭著的selector空輪詢導致CPU 100%
                    	//每次循環都把wakenUp設置成false
                    	//1.- (1)
                        select(wakenUp.getAndSet(false));
                        if (wakenUp.get()) {
                        	//此方法耗性能 wakenUp.get()預判斷下,他可能是去執行task了而不是IO???
                            selector.wakeup();
                        }
                    //不用阻塞且去執行任務了
                    default:
                        // fallthrough
                }
    
                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 {
                    	//類似Nio中循環獲事件
                        processSelectedKeys();
                    } finally {
                        // Ensure we always run tasks.
                        final long ioTime = System.nanoTime() - ioStartTime;
                        //執行非IO任務
                        //1 - (3)
                        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 select(boolean oldWakenUp) throws IOException {
        Selector selector = this.selector;
        try {
            int selectCnt = 0;
            //當前系統的納秒數
            long currentTimeNanos = System.nanoTime();
            //當前時間+延時隊列第一個任務存活時間(存活時間還沒到就大於0)
            //delayNanos方法沒有延時任務返回1納秒
            long selectDeadLineNanos = currentTimeNanos + delayNanos(currentTimeNanos);
            for (;;) {
            	//延時執行任務是否到時間
                long timeoutMillis = (selectDeadLineNanos - currentTimeNanos + 500000L) / 1000000L;
                //延時執行任務到時間了
                if (timeoutMillis <= 0) {
                    if (selectCnt == 0) {
                        selector.selectNow();
                        selectCnt = 1;
                    }
                    break;
                }
    
                // If a task was submitted when wakenUp value was true, the task didn't get a chance to call
                // Selector#wakeup. So we need to check task queue again before executing select operation.
                // If we don't, the task might be pended until select operation was timed out.
                // It might be pended until idle timeout if IdleStateHandler existed in pipeline.
                //再次檢查
                if (hasTasks() && wakenUp.compareAndSet(false, true)) {
                    selector.selectNow();
                    selectCnt = 1;
                    break;
                }
    			//阻塞等待timeoutMillis毫秒數
                int selectedKeys = selector.select(timeoutMillis);
                selectCnt ++;
    			//還沒有selectedKeys , 沒被喚醒 ,  沒有任務 , 沒有延時任務  繼續往下走
                if (selectedKeys != 0 || oldWakenUp || wakenUp.get() || hasTasks() || hasScheduledTasks()) {
                    // - Selected something,
                    // - waken up by user, or
                    // - the task queue has a pending task.
                    // - a scheduled task is ready for processing
                    break;
                }
                if (Thread.interrupted()) {
                    // Thread was interrupted so reset selected keys and break so we not run into a busy loop.
                    // As this is most likely a bug in the handler of the user or it's client library we will
                    // also log it.
                    //
                    // See https://github.com/netty/netty/issues/2426
                    if (logger.isDebugEnabled()) {
                        logger.debug("Selector.select() returned prematurely because " +
                                "Thread.currentThread().interrupt() was called. Use " +
                                "NioEventLoop.shutdownGracefully() to shutdown the NioEventLoop.");
                    }
                    selectCnt = 1;
                    break;
                }
    
                long time = System.nanoTime();
                //完整的執行完成了一個阻塞的select()操作
                if (time - TimeUnit.MILLISECONDS.toNanos(timeoutMillis) >= currentTimeNanos) {
                    // timeoutMillis elapsed without anything selected.
                    selectCnt = 1;
                  //是否循環了512次
                } else if (SELECTOR_AUTO_REBUILD_THRESHOLD > 0 &&
                        selectCnt >= SELECTOR_AUTO_REBUILD_THRESHOLD) {
                    // The selector returned prematurely many times in a row.
                    // Rebuild the selector to work around the problem.
                    logger.warn(
                            "Selector.select() returned prematurely {} times in a row; rebuilding Selector {}.",
                            selectCnt, selector);
    				//epoll  bug
    				//1.- (2)  
                    rebuildSelector();
                    selector = this.selector;
    
                    // Select again to populate selectedKeys.
                    selector.selectNow();
                    selectCnt = 1;
                    break;
                }
    
                currentTimeNanos = time;
            }
    
            if (selectCnt > MIN_PREMATURE_SELECTOR_RETURNS) {
                if (logger.isDebugEnabled()) {
                    logger.debug("Selector.select() returned prematurely {} times in a row for Selector {}.",
                            selectCnt - 1, selector);
                }
            }
        } catch (CancelledKeyException e) {
            if (logger.isDebugEnabled()) {
                logger.debug(CancelledKeyException.class.getSimpleName() + " raised by a Selector {} - JDK bug?",
                        selector, e);
            }
            // Harmless exception - log anyway
        }
    }
    
    //1.- (2) 
    public void rebuildSelector() {
    	//不是當前線程放EventLoop中執行
        if (!inEventLoop()) {
            execute(new Runnable() {
                @Override
                public void run() {
                    rebuildSelector();
                }
            });
            return;
        }
    
        final Selector oldSelector = selector;
        final Selector newSelector;
    
        if (oldSelector == null) {
            return;
        }
    
        try {
        	//新selector
            newSelector = openSelector();
        } catch (Exception e) {
            logger.warn("Failed to create a new Selector.", e);
            return;
        }
    
        // Register all channels to the new Selector.
        int nChannels = 0;
        for (;;) {
            try {
                for (SelectionKey key: oldSelector.keys()) {
                	//NioServerSocketChannel
                    Object a = key.attachment();
                    try {
                        if (!key.isValid() || key.channel().keyFor(newSelector) != null) {
                            continue;
                        }
    					
                        int interestOps = key.interestOps();
                        key.cancel();
                        //NioSocketChannel重新註冊到selector上
                        SelectionKey newKey = key.channel().register(newSelector, interestOps, a);
                        if (a instanceof AbstractNioChannel) {
                            // Update SelectionKey
                            ((AbstractNioChannel) a).selectionKey = newKey;
                        }
                        nChannels ++;
                    } catch (Exception e) {
                        logger.warn("Failed to re-register a Channel to the new Selector.", e);
                        if (a instanceof AbstractNioChannel) {
                            AbstractNioChannel ch = (AbstractNioChannel) a;
                            ch.unsafe().close(ch.unsafe().voidPromise());
                        } else {
                            @SuppressWarnings("unchecked")
                            NioTask<SelectableChannel> task = (NioTask<SelectableChannel>) a;
                            invokeChannelUnregistered(task, key, e);
                        }
                    }
                }
            } catch (ConcurrentModificationException e) {
                // Probably due to concurrent modification of the key set.
                continue;
            }
    
            break;
        }
    
        selector = newSelector;
    
        try {
            // time to close the old selector as everything else is registered to the new one
            oldSelector.close();
        } catch (Throwable t) {
            if (logger.isWarnEnabled()) {
                logger.warn("Failed to close the old Selector.", t);
            }
        }
    }
    
    
    //1 - (3)
    protected boolean runAllTasks(long timeoutNanos) {
    	//將到期延時任務加入任務隊列中
        fetchFromScheduledTaskQueue();
        //任務隊列取任務
        Runnable task = pollTask();
        if (task == null) {
            afterRunningAllTasks();
            return false;
        }
    	//timeoutNanos是任務能夠執行的時間
        final long deadline = ScheduledFutureTask.nanoTime() + timeoutNanos;
        long runTasks = 0;
        long lastExecutionTime;
        for (;;) {
            safeExecute(task);
    
            runTasks ++;
    
            // Check timeout every 64 tasks because nanoTime() is relatively expensive.
            // XXX: Hard-coded value - will make it configurable if it is really a problem.
            //每執行64次判斷下時間,看是否執行任務時間超過了設定的比值
            if ((runTasks & 0x3F) == 0) {
                lastExecutionTime = ScheduledFutureTask.nanoTime();
                //執行任務超出了時間 直接跳出  避免影響IO操作
                if (lastExecutionTime >= deadline) {
                    break;
                }
            }
    
            task = pollTask();
            if (task == null) {
                lastExecutionTime = ScheduledFutureTask.nanoTime();
                break;
            }
        }
    
        afterRunningAllTasks();
        this.lastExecutionTime = lastExecutionTime;
        return true;
    }
    
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章