4.Netty执行IO事件和非IO任务
上文说到NioEventLoop的run方法可以分为3个步骤:
轮询channel中就绪的IO事件
处理轮询出的IO事件
处理所有任务,也包括定时任务
其中步骤1已在上一节讲述,这里接着讲述下面2个步骤
IO事件与非IO任务首先看一下在步骤2和步骤3的主干代码
final int ioRatio = this.ioRatio; // 将所有任务执行完 if (ioRatio == 100) { try { processSelectedKeys(); } finally { // Ensure we always run tasks. runAllTasks(); } } else { // 记录IO事件消耗的时间,然后按比例处理分配时间处理非IO任务 final long ioStartTime = System.nanoTime(); try { processSelectedKeys(); } finally { // Ensure we always run tasks. final long ioTime = System.nanoTime() - ioStartTime; // ioRatio默认50,(100-ioRatio)/ioRatio刚好等于1,做到平均分配 runAllTasks(ioTime * (100 - ioRatio) / ioRatio); } }ioRadio是NioEventLoop的一个成员变量,用来控制分配花费在IO事件与非IO任务时间的比例。默认情况下,ioRadio是50,表示IO事件与非IO任务
将分配相同时间。而当ioRatio为100时,该值失效,不再平衡两种动作的时间分配比值。
了解了这一点,上述两种分支代码就不难理解了,我们直接进入processSelectedKeys,看看netty如何执行IO事件
先进入processSelectedKeys方法内部。
private void processSelectedKeys() { if (selectedKeys != null) { processSelectedKeysOptimized(); } else { processSelectedKeysPlain(selector.selectedKeys()); } }可以看到这里又根据selectedKeys是否为空这个条件来确定是处理优化过的keys还是普通keys。关于selectedKeys,在NioEventLoop介绍这一节中,
我们介绍了NioEventLoop的创建,在创建过程中,默认会将SelectedKeys由Hashset替换为数组实现,此处的selectedKeys正是替换过后的实现。
我们继续跟进到processSelectedKeysOptimized方法
方法内部用一个for循环处理selectedKeys。key的attchment默认是在注册时附加上去的NioServerSocketChannel和NioSocketChannel。
继续跟进processSelectedKey(k, (AbstractNioChannel) a)方法。
netty首先对selectionKey的有效性做了一个判断。当key无效时,关闭key所在的channel。当key有效时,委托NioUnsafe对象对key进行IO操作。
注意这里先进行OP_CONNECT,再执行OP_WRITE,最后执行OP_READ和OP_ACCEPT。关于Unsafe的这些IO操作留待以后分析。
processSelectedKeysPlain方法流程类似,略过
处理非IO任务由于IoRatio默认为50,我们先进入runAllTasks(ioTime * (100 - ioRatio) / ioRatio)方法。
protected boolean runAllTasks(long timeoutNanos) { // 步骤1 fetchFromScheduledTaskQueue(); // 步骤2 Runnable task = pollTask(); if (task == null) { afterRunningAllTasks(); return false; } // 步骤3 final long deadline = ScheduledFutureTask.nanoTime() + timeoutNanos; long runTasks = 0; long lastExecutionTime; for (;;) { // 步骤4 safeExecute(task); runTasks ++; // 步骤5 if ((runTasks & 0x3F) == 0) { lastExecutionTime = ScheduledFutureTask.nanoTime(); if (lastExecutionTime >= deadline) { break; } } task = pollTask(); if (task == null) { lastExecutionTime = ScheduledFutureTask.nanoTime(); break; } } // 步骤6 afterRunningAllTasks(); this.lastExecutionTime = lastExecutionTime; return true; }非IO任务的执行可以分为6个步骤
从定时任务队列聚合任务到普通任务队列
从普通队列中获取任务
计算任务执行的超时时间
安全执行任务
任务执行到一定次数,计算是否超时
执行完taskQueue普通队列里的任务后,再去执行tailTaskQueue里的任务。但目前暂时没有看到tailTaskQueue使用的地方,也许是一个扩展点吧,这里先略过。
我们一个一个步骤讲解
聚合定时任务到普通任务队列温馨提示: 本文由Jm博客推荐,转载请保留链接: https://www.jmwww.net/file/web/41526.html
- 上一篇:网站首页代码(浮动)
- 下一篇:html DOM操作