回顾
如果忘记是怎么进入一下方法的,请回去看newChild章节
io.netty.channel.nio.NioEventLoop#run
...
// 检查I/O事件
select(wakenUp.getAndSet(false));
...
// 处理上面select查到的I/O事件
processSelectedKeys();
...
// 运行上面处理的事件集
runAllTasks(ioTime * (100 - ioRatio) / ioRatio);
...
在上一节,简单走了走select方法,知道了select有阻塞和非阻塞两种方案,并且了解到Netty是如何解决jdk空轮询bug的。而这节就继续往下走,看看processSelectedKeys()方法的执行逻辑。
Netty Version:4.1.6
开始追踪
在追踪之前,先将视角拉回到io.netty.channel.nio.NioEventLoop#run
(忘记的话看newChild那章),然后进入processSelectedKeys方法:
io.netty.channel.nio.NioEventLoop#processSelectedKeys
private void processSelectedKeys() {
if (selectedKeys != null) {
processSelectedKeysOptimized(selectedKeys.flip());
} else {
processSelectedKeysPlain(selector.selectedKeys());
}
}
- 说明一下,我测试了一下,即便没有客户端连接,selectedKeys也不会为Null,它只会是一个size=0的数组。
- 这里的selectedKeys类型为SelectedSelectionKeySet,在newChild这章讲过。
- 另外,在之后的Netty版本中,这里的flip方法被弃用了,原因跟GC回收有关,详细看这个issues。
由于selectedKeys在没有连接的情况下仍不为Null,所以继续进入processSelectedKeysOptimized方法:
io.netty.channel.nio.NioEventLoop#processSelectedKeysOptimized
private void processSelectedKeysOptimized(SelectionKey[] selectedKeys) {
for (int i = 0;; i ++) {
final SelectionKey k = selectedKeys[i];
if (k == null) {
break;
}
selectedKeys[i] = null;
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) {
for (;;) {
i++;
if (selectedKeys[i] == null) {
break;
}
selectedKeys[i] = null;
}
selectAgain();
selectedKeys = this.selectedKeys.flip();
i = -1;
}
}
}
- 这里的大致逻辑:就是拿我们上一节检测到的I/O事件(封装到selectedKey中),再执行processSelectedKey逐个执行/处理。
进入processSelectedKey方法,由于代码很多,只贴一些关键的:
io.netty.channel.nio.NioEventLoop#processSelectedKey(java.nio.channels.SelectionKey, io.netty.channel.nio.AbstractNioChannel)
final AbstractNioChannel.NioUnsafe unsafe = ch.unsafe();
if (!k.isValid()) {
final EventLoop eventLoop;
try {
eventLoop = ch.eventLoop();
} catch (Throwable ignored) {
return;
}
if (eventLoop != this || eventLoop == null) {
return;
}
// close the channel if the key is not valid anymore
unsafe.close(unsafe.voidPromise());
return;
}
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();
if (!ch.isOpen()) {
return;
}
}
- 上面的ifelse大法好其实就是在判断事件的类型,然后执行不同策略。
- OP_ACCEPT很眼熟,其实是在端口绑定那节里面讲过的。
- 一般情况下parentGroup(bossGroup)负责accept事件,然后将任务交给childGroup。
- unsafe.close(unsafe.voidPromise());似乎很关键,后面可能会继续深入。
parentGroup和childGroup就是指ServerBootstrap中group方法绑定的两NioEventLoopGroup。
小结
- processSelectedKeys方法的执行逻辑很简单,就是通过ifelse大法好判断前面select方法检测出来事件的事件类型,最终调用不同的方法去处理。
- 而经过实际测试,一般情况下parentGroup是负责accept事件,然后再将任务交给childGroup执行。