读书人

Netty4服务端起动源码分析-NioEventLo

发布时间: 2013-08-27 10:20:47 作者: rapoo

Netty4服务端启动源码分析-NioEventLoop实现的线程运行逻辑
在netty服务端启动源码分析-线程创建一文中已分析SingleThreadEventExecutor所持有的线程的运行逻辑由NioEventLoop实现,那么本文就着手分析NioEventLoop实现的线程运行逻辑:

?

// NioEventLoopprotected void run() {        for (;;) {            oldWakenUp = wakenUp.getAndSet(false);            try {                if (hasTasks()) {                    selectNow();                } else {                    select();                    if (wakenUp.get()) {                        selector.wakeup();                    }                }                cancelledKeys = 0;                final long ioStartTime = System.nanoTime();                needsToSelectAgain = false;                if (selectedKeys != null) {                    processSelectedKeysOptimized(selectedKeys.flip());                } else {                    processSelectedKeysPlain(selector.selectedKeys());                }                final long ioTime = System.nanoTime() - ioStartTime;                final int ioRatio = this.ioRatio;                runAllTasks(ioTime * (100 - ioRatio) / ioRatio);                if (isShuttingDown()) {                    closeAll();                    if (confirmShutdown()) {                        break;                    }                }            } catch (Throwable t) {                logger.warn("Unexpected exception in the selector loop.", t);                // Prevent possible consecutive immediate failures that lead to                // excessive CPU consumption.                try {                    Thread.sleep(1000);                } catch (InterruptedException e) {                    // Ignore.                }            }        }    }

?分析如下:

?

    ioEventLoop执行的任务分为两大类:IO任务和非IO任务。IO任务即selectionKey中ready的事件,譬如accept、connect、read、write等;非IO任务则为添加到taskQueue中的任务,譬如之前文章中分析到的register0、bind、channelActive等任务两类任务的执行先后顺序为:IO任务->非IO任务。IO任务由processSelectedKeysOptimized(selectedKeys.flip())或processSelectedKeysPlain(selector.selectedKeys())触发;非IO任务由runAllTasks(ioTime * (100 - ioRatio) / ioRatio)触发两类任务的执行时间比由变量ioRatio控制,譬如:ioRatio=50(该值为默认值),则表示允许非IO任务执行的时间与IO任务的执行时间相等执行IO任务前,需要先进行select,以判断之前注册过的channel是否已经有感兴趣的事件ready如果任务队列中存在非IO任务,则执行非阻塞的selectNow()方法
    // NioEventLoop  void selectNow() throws IOException {        try {            selector.selectNow();        } finally {            // restore wakup state if needed            if (wakenUp.get()) {                selector.wakeup();            }        }    }
    ?否则,执行阻塞的select()方法
    // NioEventLoop   private void select() throws IOException {        Selector selector = this.selector;        try {            int selectCnt = 0;            long currentTimeNanos = System.nanoTime();            long selectDeadLineNanos = currentTimeNanos + delayNanos(currentTimeNanos);            for (;;) {                long timeoutMillis = (selectDeadLineNanos - currentTimeNanos + 500000L) / 1000000L;                if (timeoutMillis <= 0) {                    if (selectCnt == 0) {                        selector.selectNow();                        selectCnt = 1;                    }                    break;                }                int selectedKeys = selector.select(timeoutMillis);                selectCnt ++;                if (selectedKeys != 0 || oldWakenUp || wakenUp.get() || hasTasks()) {                    // Selected something,                    // waken up by user, or                    // the task queue has a pending task.                    break;                }                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);                    rebuildSelector();                    selector = this.selector;                    // Select again to populate selectedKeys.                    selector.selectNow();                    selectCnt = 1;                    break;                }                currentTimeNanos = System.nanoTime();            }            if (selectCnt > MIN_PREMATURE_SELECTOR_RETURNS) {                if (logger.isDebugEnabled()) {                    logger.debug("Selector.select() returned prematurely {} times in a row.", selectCnt - 1);                }            }        } catch (CancelledKeyException e) {            if (logger.isDebugEnabled()) {                logger.debug(CancelledKeyException.class.getSimpleName() + " raised by a Selector - JDK bug?", e);            }            // Harmless exception - log anyway        }    }
    ?下面分析阻塞的select方法:
首先执行delayNanos(currentTimeNanos):计算延迟任务队列中第一个任务的到期执行时间(即最晚还能延迟执行的时间).注意:(每个SingleThreadEventExecutor都持有一个延迟执行任务的优先队列:final Queue<ScheduledFutureTask<?>> delayedTaskQueue = new PriorityQueue<ScheduledFutureTask<?>>()),在启动线程的时候会往队列中加入一个任务)。最终的结果近似为:1秒钟-(当前时间-delayedTask创建的时间)。如果队列中没有任何任务,则默认返回1秒钟。
//SingleThreadEventExecutorprotected long delayNanos(long currentTimeNanos) {        ScheduledFutureTask<?> delayedTask = delayedTaskQueue.peek();        if (delayedTask == null) {            return SCHEDULE_PURGE_INTERVAL;        }        return delayedTask.delayNanos(currentTimeNanos);}//ScheduledFutureTaskpublic long delayNanos(long currentTimeNanos) {        return Math.max(0, deadlineNanos() - (currentTimeNanos - START_TIME));    }public long deadlineNanos() {        return deadlineNanos;    }
读书人网 >软件架构设计

热点推荐