netty学习7:NioEventLoop源码分析

我们知道 netty的事件循环组其实就是线程组,每一个线程对象都是一个NioEventLoop对象,NioEventLoop实现了Executor接口,所以我们关注NioEventLoop时 最主要关注的它的run方法

查看继承关系图

bff4e4651f284af285b03866517142f9.png

在父类io.netty.util.concurrent.SingleThreadEventExecutor#execute(java.lang.Runnable)中重写了execute方法 也很重要

public void execute(Runnable task) {//重写了execute方法
        ObjectUtil.checkNotNull(task, "task");
        execute(task, !(task instanceof LazyRunnable) && wakesUpForTask(task));
    }

/**
     * netty重写了execute方法
     * 干了几件事件
     * 1、addTask将Runnable对象添加到任务队列taskQueue中
     * 2、startThread启动该NioEventLoop线程 自旋并处理io任务
     * 3、Selector.wakeUp唤醒 select(...)阻塞的worker线程 告诉它有新的感兴趣的事件注册过来了 别睡了要开始干活了
     */
    private void execute(Runnable task, boolean immediate) {
        boolean inEventLoop = inEventLoop();
        addTask(task);//添加任务
        if (!inEventLoop) {//2中情况 主线程启动bossGroup线程   bossGroup启动workGroup线程
            startThread();//启动线程
            if (isShutdown()) {
                boolean reject = false;
                try {
                    if (removeTask(task)) {
                        reject = true;
                    }
                } catch (UnsupportedOperationException e) {
                   ...
                }
                if (reject) {
                    reject();//拒绝
                }
            }
        }

        if (!addTaskWakesUp && immediate) {
            wakeup(inEventLoop);//唤醒阻塞中的Selector
        }
    }

protected void addTask(Runnable task) {
        ObjectUtil.checkNotNull(task, "task");
        if (!offerTask(task)) {//入队
            reject(task);
        }
    }

final boolean offerTask(Runnable task) {
        if (isShutdown()) {
            reject();
        }
        return taskQueue.offer(task);
    }

private void startThread() {
        //通过cas操作保证该线程只会启动一次
        if (state == ST_NOT_STARTED) {//判断线程开启状态 是否为还未开启ST_NOT_STARTED
            if (STATE_UPDATER.compareAndSet(this, ST_NOT_STARTED, ST_STARTED)) {//cas 成ST_STARTED
                boolean success = false;
                try {
                    doStartThread();//真正开启线程
                    success = true;
                } finally {
                    if (!success) {//如果线程开启失败
                        STATE_UPDATER.compareAndSet(this, ST_STARTED, ST_NOT_STARTED);//cas 成ST_NOT_STARTED
                    }
                }
            }
        }
    }


private void doStartThread() {
        assert thread == null;
        executor.execute(new Runnable() {//执行ThreadPerTaskExecutor的start方法 start启动线程
            @Override
            public void run() {
                thread = Thread.currentThread();
                if (interrupted) {
                    thread.interrupt();
                }

                boolean success = false;
                updateLastExecutionTime();
                try {
                    SingleThreadEventExecutor.this.run();//执行自行NioEventLoop的run方法 线程组的线程自旋起来
                    success = true;
                } catch (Throwable t) {
                    logger.warn("Unexpected exception from an event executor: ", t);
                } finally {
                    //线程执行完之后关闭的事情
                    ...

                    ...

                    ...
                }
            }
        });
    }
netty重写了execute方法
干了几件事件
1、addTask将Runnable对象添加到任务队列taskQueue中
2、startThread启动该NioEventLoop线程 自旋并处理io任务
3、Selector.wakeUp唤醒 select(...)阻塞的worker线程 告诉它有新的感兴趣的事件注册过来了 让worker线程别睡了要开始干活了

 

我们看到 启动线程时是调用的io.netty.channel.nio.NioEventLoop#run方法

继续分析run方法

/**
     * run方法大致干的事情
     * 1、每一个NioEventLoop都是一个线程  都会执行自己的run方法 包括bossGroup和workerGroup
     * 2、线程是自旋阻塞的,通过selector.select(timeoutMillis)来阻塞的  唤醒的方式有两种
     *   2.1  bossGroup接收了新的客户端 将SocketChannel注册到workerGroup的selector之上时 通过selector.wakeUp唤醒
     *   2.2  selector上感兴趣的事件就绪了 有返回值 也会唤醒
     * 3、processSelectedKeys 处理Selector的就绪通道
     * 4、runAllTasks 处理taskQueue存放的队列任务
     */

    @Override
    protected void run() {
        int selectCnt = 0;
        for (;;) {
            try {
                int strategy;
                try {
                    strategy = selectStrategy.calculateStrategy(selectNowSupplier, hasTasks());
                    switch (strategy) {
                    case SelectStrategy.CONTINUE:
                        continue;

                    case SelectStrategy.BUSY_WAIT:
                        // fall-through to SELECT since the busy-wait is not supported with NIO

                    case SelectStrategy.SELECT://任务队列无数据 返回-1 走这块逻辑
                        long curDeadlineNanos = nextScheduledTaskDeadlineNanos();//获取下一个任务的任务期限事件  也就是阻塞时间
                        if (curDeadlineNanos == -1L) {
                            curDeadlineNanos = NONE; // nothing on the calendar
                        }
                        nextWakeupNanos.set(curDeadlineNanos);
                        try {
                            if (!hasTasks()) {
                                strategy = select(curDeadlineNanos);//阻塞一个相对比较长的事件 等待有其他事件被唤醒
                            }
                        } finally {
                            // This update is just to help block unnecessary selector wakeups
                            // so use of lazySet is ok (no race condition)
                            nextWakeupNanos.lazySet(AWAKE);
                        }
                        // fall through
                    default:
                    }
                } catch (IOException e) {
                    // If we receive an IOException here its because the Selector is messed up. Let's rebuild
                    // the selector and retry. https://github.com/netty/netty/issues/8566
                    rebuildSelector0();
                    selectCnt = 0;
                    handleLoopException(e);
                    continue;
                }

                selectCnt++;
                cancelledKeys = 0;
                needsToSelectAgain = false;
                final int ioRatio = this.ioRatio;
                boolean ranTasks;
                if (ioRatio == 100) {
                    try {
                        if (strategy > 0) {
                            processSelectedKeys();//处理Selector有响应的事件
                        }
                    } finally {
                        // Ensure we always run tasks.
                        ranTasks = runAllTasks();//处理taskQueue存放的队列任务
                    }
                } else if (strategy > 0) {
                    final long ioStartTime = System.nanoTime();
                    try {
                        processSelectedKeys();//处理Selector有响应的事件
                    } finally {
                        // Ensure we always run tasks.
                        final long ioTime = System.nanoTime() - ioStartTime;
                        ranTasks = runAllTasks(ioTime * (100 - ioRatio) / ioRatio);//处理taskQueue存放的队列任务
                    }
                } else {
                    ranTasks = runAllTasks(0); // This will run the minimum number of tasks
                }

                if (ranTasks || strategy > 0) {
                    if (selectCnt > MIN_PREMATURE_SELECTOR_RETURNS && logger.isDebugEnabled()) {
                        logger.debug("Selector.select() returned prematurely {} times in a row for Selector {}.",
                                selectCnt - 1, selector);
                    }
                    selectCnt = 0;
                } else if (unexpectedSelectorWakeup(selectCnt)) { // Unexpected wakeup (unusual case)
                    selectCnt = 0;
                }
            } catch (CancelledKeyException e) {
                // Harmless exception - log anyway
                if (logger.isDebugEnabled()) {
                    logger.debug(CancelledKeyException.class.getSimpleName() + " raised by a Selector {} - JDK bug?",
                            selector, e);
                }
            } catch (Error e) {
                throw (Error) e;
            } catch (Throwable t) {
                handleLoopException(t);
            } finally {
                // Always handle shutdown even if the loop processing threw an exception.
                try {
                    if (isShuttingDown()) {
                        closeAll();
                        if (confirmShutdown()) {
                            return;
                        }
                    }
                } catch (Error e) {
                    throw (Error) e;
                } catch (Throwable t) {
                    handleLoopException(t);
                }
            }
        }
    }


private void processSelectedKeys() {
        if (selectedKeys != null) {
            processSelectedKeysOptimized();//有就绪事件 走该方法
        } else {
            processSelectedKeysPlain(selector.selectedKeys());
        }
    }

private void processSelectedKeysOptimized() {
        for (int i = 0; i < selectedKeys.size; ++i) {
            final SelectionKey k = selectedKeys.keys[i];
            selectedKeys.keys[i] = null;
final Object a = k.attachment();//拿到NioServerSocketChannel或者NioSocketChannel
            if (a instanceof AbstractNioChannel) {
                processSelectedKey(k, (AbstractNioChannel) a);//处理select事件
            } 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;
            }
        }
    }



private void processSelectedKey(SelectionKey k, AbstractNioChannel ch) {
        final AbstractNioChannel.NioUnsafe unsafe = ch.unsafe();
        if (!k.isValid()) {//key失效
            ...
            return;
        }

        try {
            int readyOps = k.readyOps();
            
            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();
            }

            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();//强制刷新
            }

            if ((readyOps & (SelectionKey.OP_READ | SelectionKey.OP_ACCEPT)) != 0 || readyOps == 0) {//读事件或者 接收事件
                unsafe.read();//读事件 bossGroup和workerGroup不一样 参考前文的unafe分析
            }
        } catch (CancelledKeyException ignored) {
            unsafe.close(unsafe.voidPromise());
        }
    }


protected boolean runAllTasks() {//处理任务队列中所有的任务
        assert inEventLoop();
        boolean fetchedAll;
        boolean ranAtLeastOne = false;

        do {
            fetchedAll = fetchFromScheduledTaskQueue();
            if (runAllTasksFrom(taskQueue)) {//处理taskQueue中所有的任务
                ranAtLeastOne = true;
            }
        } while (!fetchedAll); // keep on processing until we fetched all scheduled tasks.

        if (ranAtLeastOne) {
            lastExecutionTime = ScheduledFutureTask.nanoTime();
        }
        afterRunningAllTasks();
        return ranAtLeastOne;
    }

protected final boolean runAllTasksFrom(Queue<Runnable> taskQueue) {
        Runnable task = pollTaskFrom(taskQueue);//从任务队列中poll获取task
        if (task == null) {
            return false;
        }
        for (;;) {
            safeExecute(task);//安全执行
            task = pollTaskFrom(taskQueue);
            if (task == null) {//
                return true;
            }
        }
    }

protected static void safeExecute(Runnable task) {
        try {
            task.run();//就是调用task的run方法
        } catch (Throwable t) {
            logger.warn("A task raised an exception. Task: {}", task, t);
        }
    }

 

总结run方法:

  1、每一个NioEventLoop都是一个线程  都会执行自己的run方法 包括bossGroup和workerGroup
   2、线程是自旋阻塞的,通过selector.select(timeoutMillis)来阻塞的  唤醒的方式有两种
      2.1  bossGroup接收了新的客户端 将SocketChannel注册到workerGroup的selector之上时 通过selector.wakeUp唤醒
      2.2  selector上感兴趣的事件就绪了 有返回值 也会唤醒
   3、processSelectedKeys 处理Selector的就绪通道
   4、runAllTasks 处理taskQueue存放的队列任务

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值