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承担了两个方面的职责:
- 处理io事件
- 处理任务
然后一起来看它是怎么处理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));
从这几行代码中我们可以得到的信息有:
- nio中的Selector中的selectKey被通过反射的方式替换成了SelectedSelectionKeySet(SelectedSelectionKeySet内部使用数组存储,操作时的时间复杂度更低)
- 因为我们的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,并设置它的处理器和注册操作。