Netty源码分析6:NioEventLoop

NioEventLoop基本介绍

在构建Netty服务时,首先需要创建一个NioEventLoopGroup,即如下代码:

···
NioEventLoopGroup boss = new NioEventLoopGroup(1);
NioEventLoopGroup worker = new NioEventLoopGroup();
···
  • 初始化时机:NioEventLoopGroup内部定义了一个EventExecutor[] children成员变量(NioEventLoop是EventExecutor的子类),在初始化NioEventLoopGroup时也会初始化children,NioEventLoopGroup和NioEventLoop之间的关系有点类似于线程池和线程之间的关系,而NioEventLoop本身也具备线程池的特性,因此她们之间的关系类似线程池里面嵌套线程池的关系。
  • 主要功能:NioEventLoop关联的线程会循环的调用selector.select()方法监听相关Channel上的事件,此外它还会基于一定的策略执行外部提交的任务。相关的Channel来自boss基于轮询的分发,保证了各个NioEventLoop之间的负载均衡。而外部任务主要包括向Channel中写数据以及用户提交的自定义任务
  • 上述代码中boss定义一个NioEventLoop,这因为boss通常用于接收客户端连接请求。当处理链接请求出现瓶颈时可以基于多端口监听为boss创建更多的NioEventLoop;worker默认情况下将创建:处理器核数 * 2个NioEventLoop,boss获取到客户端连接请求后会将建立的Channel注册到worker中的一个NioEventLoop上,由该NioEventLoop来处理该Channel上的IO。
  • NioEventLoop的类图结构如下:
    在这里插入图片描述
    • 实现了jdk的ScheduledExecutorService具备线程池的特性,下面简单的说明每个接口的功能作用
    • EventExecutorGroup:实现了该接口,说明当我们调用EventExecutorGroup上的方法时实际上基于轮询方式调用内部EventExecutor的对应的方法。该接口提供了优雅关闭EventExecutor的相关方法,
      在这里插入图片描述
      • next():轮询内部EventExecutor
      • shutdownGracefully():优雅关闭当前EventExecutorGroup,即优雅关闭内部EventExecutor
      • terminationFuture():获取异步结束结果,当EventExecutorGroup关闭完成后该结果返回,在此之前在获取结果将被阻塞
      • isShuttingDown():返回是否关闭完成
      • 其他方法继承自jdk的接口的接口,仅更改了返回值返回Netty定义的高级异步结果(继承自jdk的Future)
    • EventExecutor:提供用于判断一个线程是否是EventExecutor关联的内部线程。为避免多线程问题,对EventExecutor及其子类(NioEventLoop)的相关的配置方法的调用的通常基于任务形势提交到NioEventLoop让其关联线程执行,例如当外部线程将一个Channel注册到NioEventLoop时实际上并没有直接调用NioEventLoop的相关方法而是封装成任务提交到NioEventLoop,通过EventExecutor提供的方法判断。此外也提供了快速构建成功的异步结果和失败的异步结果。
      在这里插入图片描述
      • inEventLoop():判断当前线程是否属于EventExecutor关联的内部线程
      • next():固定返回当前EventExecutor
      • parent():返回对应的EventLoopGroup
      • 其他方法用于创建异步结果
    • EventLoopGroup:与EventExecutorGroup相似,EventExecutorGroup提供了提交任务相关的方法,而EventLoopGroup提供了注册Channel的方法,此外还提供了一个用于轮询内部EventExecutor的方法
      在这里插入图片描述
      • next():轮询内部EventExecutor
      • register():将Channel注册到next()返回的EventExecutor上(基于任务提交的方式)
    • OrderedEventExecutor:标记接口没有方法,以有序串行的方式处理提交的任务。
    • EventLoop:事件循环,实际上仅仅是覆盖了弗雷德group()方法返回一个更具体地子类EventLoopGroup
      在这里插入图片描述

NioEventLoop初始化过程

在初始化NioEventLoopGroup时会调用NioEventLoop#newChild()方法创建NioEventLoop。

	//NioEventLoopGroup类的newChild方法
    protected EventLoop newChild(Executor executor, Object... args) throws Exception {
        SelectorProvider selectorProvider = (SelectorProvider) args[0];
        SelectStrategyFactory selectStrategyFactory = (SelectStrategyFactory) args[1];
        RejectedExecutionHandler rejectedExecutionHandler = (RejectedExecutionHandler) args[2];
        EventLoopTaskQueueFactory taskQueueFactory = null;
        EventLoopTaskQueueFactory tailTaskQueueFactory = null;

        int argsLength = args.length;
        if (argsLength > 3) {
            taskQueueFactory = (EventLoopTaskQueueFactory) args[3];
        }
        if (argsLength > 4) {
            tailTaskQueueFactory = (EventLoopTaskQueueFactory) args[4];
        }
        return new NioEventLoop(this, executor, selectorProvider,
                selectStrategyFactory.newSelectStrategy(),
                rejectedExecutionHandler, taskQueueFactory, tailTaskQueueFactory);
    }
  • 具体入参如下:
    • this:NioEventLoopGroup实例
    • executor:ThreadPerTaskExecutor
    • selectorProvider:jdk的selectorProvider多路复用器提供商
    • selectStrategyFactory.newSelectStrategy():DefaultSelectStrategy,默认的选择策略,通过该选择策略决定NioEventLoop是执行外部提交任务还是监听Channel事件
    • rejectedExecutionHandler:当使用有界队列暂存任务时,如果队列已满使用该处理器来决定如何处理,默认抛出异常
    • taskQueueFactory:任务队列,及通过execute/submit提交的任务将保存到该队列
    • tailTaskQueueFactory:Netty暂时还没有使用到,设计目的是为了定义一个比taskQueueFactory优先级低的任务队列
  • NioEventLoop内部成员变量及静态代码块说明
    在这里插入图片描述
    • DEFAULT_MAX_PENDING_TASKS:任务队列的最大值,默认Integer.MAX_VALUE
    • AWAKE=-1,与nextWakeupNanos共同作用,当nextWakeupNanos的值为AWAKE时将通过selector.wakeup将线程从阻塞监听事件中唤醒以执行外部提交任务。此外当AWAKE=-1时表示线程正在运行,外部提交的计划任务将直接放入队列并不会添加唤醒任务唤醒线程,NONE与之对应,select()操作将不设置超时时间
    • cancelledKeys:当通道关闭时,对端关闭通道将触发deregister事件,最终将会将Channel从selector中删除,该值统计selector上删除的次数,当达到CLEANUP_INTERVAL=256时将出发selector重新构建selector。注意:取消注册是以提交任务的形式被执行的,因此该值的累计只有在短时间内大量Channel取消时才会有可能,因为所有任务执行完成下次再执行任务时会重置为0
    • DISABLE_KEY_SET_OPTIMIZATION:禁用优化键功能,默认false,将使用可以扩容的SelectedSelectionKeySet集合替换Selector内部的selectedKeys和publicSelectedKeys类型
    • ioRatio:io时间占比,默认50%
    • MIN_PREMATURE_SELECTOR_RETURNS=3,产生空轮询的次数限制,超过将导致多路复用器重建,默认512,当自定义低于3时为3
    • needsToSelectAgain:在处理io事件前需要再次执行selectNow()来重新获取事件,当产生通道关闭时为true,仅仅在处理事件期间有用,循环体内会别清空
    • provider:SelectorProvider
    • selectedKeys:SelectedSelectionKeySet,即优化的键集
    • selectNowSupplier:selectNow()的实现,默认使用jdk的selectNow(),此外window/linux均有对应的实现
    • selector:Selector,内部使用SelectedSelectionKeySet替代原始的HashSet
    • unwrappedSelector:Selector,内部使用原始的HashSet

实例化源码分析

  • 构造器没有复杂初始化逻辑,都是简单的赋值操作
    NioEventLoop(NioEventLoopGroup parent, Executor executor, SelectorProvider selectorProvider,
                 SelectStrategy strategy, RejectedExecutionHandler rejectedExecutionHandler,
                 EventLoopTaskQueueFactory taskQueueFactory, EventLoopTaskQueueFactory tailTaskQueueFactory) {
                 //newTaskQueue()将使用MpscUnboundedArrayQueue缓存任务队列,该队列效率比BlockingQueue高
                 //继续调用父类构造器
        super(parent, executor, false, newTaskQueue(taskQueueFactory), newTaskQueue(tailTaskQueueFactory),
                rejectedExecutionHandler);
        this.provider = ObjectUtil.checkNotNull(selectorProvider, "selectorProvider");
        this.selectStrategy = ObjectUtil.checkNotNull(strategy, "selectStrategy");
        //创建selector多路复用器
        final SelectorTuple selectorTuple = openSelector();
        this.selector = selectorTuple.selector;//键集优化后的多路复用器
        this.unwrappedSelector = selectorTuple.unwrappedSelector;//原始多路复用器
    }
	
	//父类构造器,addTaskWakesUp=false表示外部添加任务将会通过selector.wakeup唤醒阻塞监听
    protected SingleThreadEventLoop(EventLoopGroup parent, Executor executor,
                                    boolean addTaskWakesUp, Queue<Runnable> taskQueue, Queue<Runnable> tailTaskQueue,
                                    RejectedExecutionHandler rejectedExecutionHandler) {
        //继续调用父类构造器                            
        super(parent, executor, addTaskWakesUp, taskQueue, rejectedExecutionHandler);
        tailTasks = ObjectUtil.checkNotNull(tailTaskQueue, "tailTaskQueue");
    }

    protected SingleThreadEventExecutor(EventExecutorGroup parent, Executor executor,
                                        boolean addTaskWakesUp, Queue<Runnable> taskQueue,
                                        RejectedExecutionHandler rejectedHandler) {
        super(parent);//简单的在父类构造器中将parent赋值给成员变量
        this.addTaskWakesUp = addTaskWakesUp;
        this.maxPendingTasks = DEFAULT_MAX_PENDING_EXECUTOR_TASKS;
        //这里的传入的executor是NioEventLoopGroup中传入的ThreadPerTaskExecutor,通过ThreadExecutorMap.apply做了封装
        this.executor = ThreadExecutorMap.apply(executor, this);
        this.taskQueue = ObjectUtil.checkNotNull(taskQueue, "taskQueue");
        this.rejectedExecutionHandler = ObjectUtil.checkNotNull(rejectedHandler, "rejectedHandler");
    }
  • ThreadExecutorMap.apply(executor, this):该方法调用将传入的ThreadPerTaskExecutor进行了封装,具体如下:
    public static Executor apply(final Executor executor, final EventExecutor eventExecutor) {
        ObjectUtil.checkNotNull(executor, "executor");
        ObjectUtil.checkNotNull(eventExecutor, "eventExecutor");
        //返回一个新的Executor,内部还是通过ThreadPerTaskExecutor来执行任务,
        //仅仅是通过apply()方法封装了提交的任务
        return new Executor() {
            @Override
            public void execute(final Runnable command) {
                executor.execute(apply(command, eventExecutor));
            }
        };
    }
	//封装提交任务,在原有任务开头和结尾处增加了额外逻辑
	//其目的是为了在任务内部获取到线程关联的NioEventLoop
    public static Runnable apply(final Runnable command, final EventExecutor eventExecutor) {
        ObjectUtil.checkNotNull(command, "command");
        ObjectUtil.checkNotNull(eventExecutor, "eventExecutor");
        return new Runnable() {
            @Override
            public void run() {
            	//任务开始前将当前NioEventLoop放入本地线程变量中
                setCurrentEventExecutor(eventExecutor);
                try {
                    command.run();
                } finally {
            		//任务结束前将当前NioEventLoop从本地线程变量中移除
                    setCurrentEventExecutor(null);
                }
            }
        };
    }

NioEventLoop是如何工作的

在Netty的服务端编程中,通过调用ServerBootstrap#bind()方法在内部将会向boss(NioEventLoop)提交一个注册任务,此时将触发NioEventLoop开启一个线程开始工作,因此将从NioEventLoop#execute()开始分析,execute()方法由父类SingleThreadEventExecutor实现,源码如下:

execute()

    public void execute(Runnable task) {
        ObjectUtil.checkNotNull(task, "task");
        //方法重载
        execute(task, !(task instanceof LazyRunnable) && wakesUpForTask(task));
    }
    private void execute(Runnable task, boolean immediate) {
    	//判断当前线程是否是NioEventLoop关联线程,提交的注册任务属于主线程因此未false
        boolean inEventLoop = inEventLoop();
        //任务入队
        addTask(task);
        if (!inEventLoop) {
        	//启动线程
            startThread();
            if (isShutdown()) {
                boolean reject = false;
                try {
                    if (removeTask(task)) {
                        reject = true;
                    }
                } catch (UnsupportedOperationException e) {
                    // The task queue does not support removal so the best thing we can do is to just move on and
                    // hope we will be able to pick-up the task before its completely terminated.
                    // In worst case we will log on termination.
                }
                if (reject) {
                    reject();
                }
            }
        }
		//addTaskWakesUp = false(构造器),immediate = true;
        if (!addTaskWakesUp && immediate) {
        	//唤醒阻塞监听
            wakeup(inEventLoop);
        }
    }

inEventLoop():判断当前线程是否为NioEventLoop关联线程

提交第一个任务时线程还没有启动,此时为null,因此该判断为false

    public boolean inEventLoop() {
        return inEventLoop(Thread.currentThread());
    }
    public boolean inEventLoop(Thread thread) {
        return thread == this.thread;
    }

addTask():任务入队待执行

    protected void addTask(Runnable task) {
        ObjectUtil.checkNotNull(task, "task");
        //入队失败、执行拒绝逻辑,及构造器如惨重的rejectedExecutionHandler,默认抛出异常
        if (!offerTask(task)) {
            reject(task);
        }
    }

startThread():启动NioEventLoop关联线程

    private void startThread() {
        if (state == ST_NOT_STARTED) {
        	//cas将NioEventLoop的状态由未开始置为开始状态
            if (STATE_UPDATER.compareAndSet(this, ST_NOT_STARTED, ST_STARTED)) {
                boolean success = false;
                try {
                    doStartThread();
                    success = true;
                } finally {
                    if (!success) {
                    	//如果启动失败将状态还原为未开始
                        STATE_UPDATER.compareAndSet(this, ST_STARTED, ST_NOT_STARTED);
                    }
                }
            }
        }
    }

    private void doStartThread() {
        assert thread == null;
        //这里的executor属于ThreadPerTaskExecutor,其execute()方法见下方,将启动一个
        //FastThreadLocalThread线程来执行该任务(FastThreadLocalThread继承Thread)
        executor.execute(new Runnable() {
            @Override
            public void run() {
            	//将当前线程赋值给NioEventLoop的关联线程
                thread = Thread.currentThread();
                if (interrupted) {
                    thread.interrupt();
                }

                boolean success = false;
                //记录该线程自服务启动后的时间间隔
                updateLastExecutionTime();
                try {
                	//即执行NioEventLoop#run(),该方法为无线循环,知道外部发送关闭命令
                    SingleThreadEventExecutor.this.run();
                    success = true;
                } catch (Throwable t) {
                    logger.warn("Unexpected exception from an event executor: ", t);
                } finally {
                //···省略掉关闭逻辑
                }
        });
    }

ThreadPerTaskExecutor#execute():该方法将启动一个线程来执行提交的任务

public final class ThreadPerTaskExecutor implements Executor {
    private final ThreadFactory threadFactory;

    public ThreadPerTaskExecutor(ThreadFactory threadFactory) {
        this.threadFactory = ObjectUtil.checkNotNull(threadFactory, "threadFactory");
    }

    @Override
    public void execute(Runnable command) {
    	//threadFactory默认为DefaultThreadFactory
        threadFactory.newThread(command).start();
    }
}

public class DefaultThreadFactory implements ThreadFactory {

    private static final AtomicInteger poolId = new AtomicInteger();

    private final AtomicInteger nextId = new AtomicInteger();//线程名id不重名
    private final String prefix;//线程名前缀:NioEventLoopGroup-1
    private final boolean daemon;//守护线程,默认false
    private final int priority;//默认值5
    protected final ThreadGroup threadGroup;//所在线程组
	
	···
	忽略部分无关代码
	···

    @Override
    public Thread newThread(Runnable r) {
    	//创建线程
        Thread t = newThread(FastThreadLocalRunnable.wrap(r), prefix + nextId.incrementAndGet());
        try {
            if (t.isDaemon() != daemon) {
                t.setDaemon(daemon);
            }

            if (t.getPriority() != priority) {
                t.setPriority(priority);
            }
        } catch (Exception ignored) {
            // Doesn't matter even if failed to set.
        }
        return t;
    }
	//创建FastThreadLocalThread,该类继承Thread,优化了ThreadLocal访问速度
    protected Thread newThread(Runnable r, String name) {
        return new FastThreadLocalThread(threadGroup, r, name);
    }
}

NioEventLoop#run():循环阻塞监听Channel事件并执行外部提交任务

至此NioEventLoop关联线程已经启动起来,下面分析run方法

    protected void run() {
        int selectCnt = 0;
        for (;;) {
            try {
                int strategy;
                try {
                	//计算选择策略,如果有任务则由selectNowSupplier提供的值确定,否则执行阻塞监听
                	//selectNowSupplier将执行selectNow() >= 0
                	//第一次循环肯定有任务,因此将跳过select()阻塞去执行任务
                	//strategy >= 0或=-1(阻塞监听case)
                    strategy = selectStrategy.calculateStrategy(selectNowSupplier, hasTasks());
                    switch (strategy) {
                    case SelectStrategy.CONTINUE://-2不可能
                        continue;

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

                    case SelectStrategy.SELECT://任务执行完成后再次循环时将走该分支
                    	//不存在计划任务,curDeadlineNanos = -1
                        long curDeadlineNanos = nextScheduledTaskDeadlineNanos();
                        if (curDeadlineNanos == -1L) {
                            curDeadlineNanos = NONE; // 非超时阻塞、直到事件到达
                        }
                        //此处设置无限阻塞后在提价schedule任务时将尝试唤醒
                        nextWakeupNanos.set(curDeadlineNanos);
                        try {
                            if (!hasTasks()) {//最后在检测是否存在任务,之后阻塞监听
                                strategy = select(curDeadlineNanos);
                            }
                        } finally {
                            // 意味着当前正在执行任务,因此外部任务到达时无需唤醒
                            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++;//阻塞监听次数+1;
                cancelledKeys = 0;//重置通道关闭累计值
                needsToSelectAgain = false;//重置需要再次selectNow()
                final int ioRatio = this.ioRatio;
                boolean ranTasks;
                if (ioRatio == 100) {
                    try {
               			 //io占比100%时如果存在Channel事件处理事件
                        if (strategy > 0) {
                            processSelectedKeys();
                        }
                    } finally {
                        // 执行所有任务
                        ranTasks = runAllTasks();
                    }
                } else if (strategy > 0) {
                    final long ioStartTime = System.nanoTime();
                    try {
                    	//处理io事件
                        processSelectedKeys();
                    } finally {
                        // 计算io时间
                        final long ioTime = System.nanoTime() - ioStartTime;
                        //根据ioRatio计算任务应该执行的时间
                        ranTasks = runAllTasks(ioTime * (100 - ioRatio) / ioRatio);
                    }
                } else {
                	//执行时间为0,将最多执行64个任务后不在执行后续任务
                    ranTasks = runAllTasks(0);
                }
				//每次循环只要执行了任务或io事件,则selectCnt重置
                if (ranTasks || strategy > 0) {
                	//如果空轮询了3次以上后不再空轮询则重置selectCnt
                    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;//重置
                  //如果由于中断唤醒导致空轮询则重置清零则重置selectCnt
                  //否则如果空轮询超过512次则认为产生jdk Selector空轮询bug,重新构建Selector并充值selectCnt
                } 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 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 e;
                } catch (Throwable t) {
                    handleLoopException(t);
                }
            }
        }
    }
processSelectedKeys():遍历Channel就绪事件

如果在遍历期间发生了大量Channel关闭事件(超过256)则清除未处理的通道事件并从新selectNow()获取

    private void processSelectedKeys() {
        if (selectedKeys != null) {
            processSelectedKeysOptimized();//默认开启select()优化,走该逻辑
        } else {
            processSelectedKeysPlain(selector.selectedKeys());
        }
    }
    private void processSelectedKeysOptimized() {
    	//遍历每一个SelectedKey
        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;

            final Object a = k.attachment();
			//selectionKey = javaChannel().register(eventLoop().unwrappedSelector(), 0, this);
			//注册的时候附加的是AbstractNioChannel
            if (a instanceof AbstractNioChannel) {
            	//处理通道就绪事件
                processSelectedKey(k, (AbstractNioChannel) a);
            } else {
            	//如果注册附加的是NioTask,则后面讲无法获取到AbstractNioChannel
            	//只能固定的执行task定义的内容,目前没有发现有具体实现类
                @SuppressWarnings("unchecked")
                NioTask<SelectableChannel> task = (NioTask<SelectableChannel>) a;
                processSelectedKey(k, task);
            }
			//如果在本循环期间发生了很多(超过256)Channel close事件,则needsToSelectAgain将被值为true
			//进行selectNow()更新事件
            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
                //清空未处理的Channel事件重新selectNow()获取,防止
                selectedKeys.reset(i + 1);
				//重新selectNow()
                selectAgain();
                i = -1;
            }
        }
    }
processSelectedKey():处理具体Channel事件,区分类型:OP_READ,OP_WIRTE_OPCONNECT,OP_ACCEPT
    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.
                //确保通道绑定了具体的EventLoop
                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) {
                // close the channel if the key is not valid anymore
                //关闭Channel
                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.
            //处理Channel上的链接事件,该事件仅在客户端才会发生,当客户端执行Channel.connect()后后监听
            //CONNECT事件,监听到该时间后调用finishConnect()完成连接,否则如果connect()之后直接执行
            //read或write操作的话可能会抛出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);
				//jdk nio的finishConnect()将返回boolean值确定是否连接完成,unsafe封装了该操作,如果
				//到此还是false的话将抛出异常
                unsafe.finishConnect();
            }

            // Process OP_WRITE first as we may be able to write some queued buffers and so free memory.
            //处理Channel上的写事件,将Netty定义的写缓冲区上的数据flush到对端
            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
            //处理Channel上的读请求和链接请求,事实上读请求和连接请求都是从通道中获取数据,后者获取到channel数据而已
            if ((readyOps & (SelectionKey.OP_READ | SelectionKey.OP_ACCEPT)) != 0 || readyOps == 0) {
                unsafe.read();
            }
        } catch (CancelledKeyException ignored) {
            unsafe.close(unsafe.voidPromise());
        }
    }

到此NioEventLoop的主流程已经结束,整理一下期间的一些方法的说明如下:

  • run() -> nextScheduledTaskDeadlineNanos():在select()阻塞监听时通过该方法获取计最近的计划任务的执行时间,以便阻塞监听该时间后恢复执行计划任务,如果没有计划任务将直接阻塞监听直至有就绪事件到达
  • run() -> runAllTasks()、runAllTasks(long times)、runAllTasks(0):用于执行外部提交任务,无论ioRatio比例iwei多少,都要优先执行Channel事件,runAllTasks()将执行所有任务,runAllTasks(long times)将在截至时间到达后返回,实际上以64个任务为执行单元
  • processSelectedKeysOptimized和processSelectedKeysPlain:都是遍历就绪事件执行,区别在于前者由于优化了Selector,需要执行自定义的优化逻辑
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值