netty源码--NioEventLoop的run方法

5 篇文章 0 订阅
4 篇文章 0 订阅

NioEventLoop


源码版本4.1.66

NioEventLoop的作用

从上一节ServerBootstrap的启动流程中我们可以知道:NioEventLoop承担的一个职责是执行队列中的任务,同时也需要为绑定到自己Selector上面的Channel处理IO事件

同时也介绍了,在 SingleThreadEventExecutor#doStartThread 方法中,正是由一个线程启动了NioEventLoop的run方法来执行这些操作,下面我们就来一起看一下NioEventLoop的run方法:

@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:
                case SelectStrategy.SELECT:
                    long curDeadlineNanos = nextScheduledTaskDeadlineNanos();
                    if (curDeadlineNanos == -1L) {
                        curDeadlineNanos = NONE; // nothing on the calendar
                    }
                    nextWakeupNanos.set(curDeadlineNanos);
                    try {
                        if (!hasTasks()) {
                            strategy = select(curDeadlineNanos);
                        }
                    } finally {
                        nextWakeupNanos.lazySet(AWAKE);
                    }
                default:
                }
            } catch (IOException e) {
                // 省略部分代码...
            }

            selectCnt++;
            cancelledKeys = 0;
            needsToSelectAgain = false;
            final int ioRatio = this.ioRatio;
            boolean ranTasks;
            // 按照比例执行io事件和task
            if (ioRatio == 100) {
                try {
                    if (strategy > 0) {
                        processSelectedKeys();// 获取就绪事件
                    }
                } finally {
                    ranTasks = runAllTasks();// 执行任务
                }
            } else if (strategy > 0) {
                final long ioStartTime = System.nanoTime();
                try {
                    processSelectedKeys();// 获取就绪事件
                } finally {
                    // // 执行任务
                    final long ioTime = System.nanoTime() - ioStartTime;
                    ranTasks = runAllTasks(ioTime * (100 - ioRatio) / ioRatio);
                }
            } else {
                ranTasks = runAllTasks(0); // 只执行任务
            }
            // 省略一些代码
    }
}

通过这个方法不难看出,NioEventLoop承担了两个方面的职责:

  1. 处理io事件
  2. 处理任务

然后一起来看它是怎么处理IO事件的:

processSelectedKeys()

processSelectedKeys()方法内容如下:

private void processSelectedKeys() {
    if (selectedKeys != null) {
        processSelectedKeysOptimized();// 优化
    } else {
        processSelectedKeysPlain(selector.selectedKeys());
    }
}

之前在 NioEventLoopGroup的初始化 中提到:NioEventLoop在初始化时候,会获取到Java层面的Selector,然后将该Selector封装成一个SelectedSelectionKeySetSelector,这个过程我们先看一下(NioEventLoop#openSelector):

...
unwrappedSelector = provider.openSelector();// 获取Selecotr
...
// 反射获取field
Field selectedKeysField = selectorImplClass.getDeclaredField("selectedKeys");
Field publicSelectedKeysField = selectorImplClass.getDeclaredField("publicSelectedKeys");
...    
final SelectedSelectionKeySet selectedKeySet = new SelectedSelectionKeySet();
...
// 通过反射修改两个字段的值
selectedKeysField.set(unwrappedSelector, selectedKeySet);
publicSelectedKeysField.set(unwrappedSelector, selectedKeySet);
...
// 将 selectedKeySet 赋值给 selectKeys
selectedKeys = selectedKeySet;
// 最后将Selector封装成一个 SelectedSelectionKeySetSelector
return new SelectorTuple(unwrappedSelector,
                                new SelectedSelectionKeySetSelector(unwrappedSelector, selectedKeySet));

从这几行代码中我们可以得到的信息有:

  1. nio中的Selector中的selectKey被通过反射的方式替换成了SelectedSelectionKeySet(SelectedSelectionKeySet内部使用数组存储,操作时的时间复杂度更低)
  2. 因为我们的NioEventLoop中的变量selectedKeys也被Selector持有了引用,所以Selecotr.select获取到的事件我们也可以直接通过selectKeys拿到

知道了Netty针对Selector做的优化,下面我们就可以继续看processSelectedKeysOptimized()这个方法内部做的事情了:

processSelectedKeysOptimized()

这里利用数组的索引和有序性对SelectionKey的遍历做了优化:

private void processSelectedKeysOptimized() {
    // 遍历selectionKey
    for (int i = 0; i < selectedKeys.size; ++i) {
        final SelectionKey k = selectedKeys.keys[i];
        // 及时置空便于GC
        selectedKeys.keys[i] = null;
        // 因为之前的设置 所以这里肯定是NioServerSocketChannel
        final Object a = k.attachment();

        if (a instanceof AbstractNioChannel) {
            processSelectedKey(k, (AbstractNioChannel) a);
        } else {
            @SuppressWarnings("unchecked")
            NioTask<SelectableChannel> task = (NioTask<SelectableChannel>) a;
            processSelectedKey(k, task);
        }

        if (needsToSelectAgain) {
            selectedKeys.reset(i + 1);
            selectAgain();
            i = -1;
        }
    }
}

这里主要是获取通道然后调用processSelectedKey方法进行处理。

processSelectedKey()

该方法用来处理IO事件,并根据事件调用责任链中不同的链路:

/**
 * 根据类型对事件作出处理
 */
private void processSelectedKey(SelectionKey k, AbstractNioChannel ch) {
    final AbstractNioChannel.NioUnsafe unsafe = ch.unsafe();
    if (!k.isValid()) {
        // 省略部分代码...
    }

    try {
        // 根据事件调用不同的通道
        int readyOps = k.readyOps();
        if ((readyOps & SelectionKey.OP_CONNECT) != 0) {
            int ops = k.interestOps();
            ops &= ~SelectionKey.OP_CONNECT;
            k.interestOps(ops);

            unsafe.finishConnect();
        }

        if ((readyOps & SelectionKey.OP_WRITE) != 0) {
            ch.unsafe().forceFlush();
        }

        if ((readyOps & (SelectionKey.OP_READ | SelectionKey.OP_ACCEPT)) != 0 || readyOps == 0) {
            unsafe.read();
        }
    } catch (CancelledKeyException ignored) {
        unsafe.close(unsafe.voidPromise());
    }
}

假如此时有一个客户端连接,那么将会进入到unsafe.read()中,并会执行到ServerBootstrapAcceptor的方法:

@Override
@SuppressWarnings("unchecked")
public void channelRead(ChannelHandlerContext ctx, Object msg) {
    final Channel child = (Channel) msg;

    child.pipeline().addLast(childHandler);

    setChannelOptions(child, childOptions, logger);
    setAttributes(child, childAttrs);

    try {
        childGroup.register(child).addListener(new ChannelFutureListener() {
            @Override
            public void operationComplete(ChannelFuture future) throws Exception {
                if (!future.isSuccess()) {
                    forceClose(child, future.cause());
                }
            }
        });
    } catch (Throwable t) {
        forceClose(child, t);
    }
}

在该方法中,会获取到客户端通道SocketChannel,并设置它的处理器和注册操作。

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值