空轮询的产生
由于 JDK 自身的问题,再 NIO 操作的时候,可能会产生空轮询问题,当产生空轮询的时候,int selectedKeys selector.select() 并不会阻塞,会直接返回 0,并且会一直重复,导致服务不可用
Netty 判断是否是空轮询利用两个属性,一个时间
- 两个属性
- 一个是循环次数 selectCnt,每次循环 selectCnt 都会自增 1
- 一个是阈值 SELECTOR_AUTO_REBUILD_THRESHOLD
- 一个时间
- 判断执行时间是否超过正常阻塞时间 timeoutMillis
int selectorAutoRebuildThreshold = SystemPropertyUtil.getInt("io.netty.selectorAutoRebuildThreshold", 512);
SELECTOR_AUTO_REBUILD_THRESHOLD = selectorAutoRebuildThreshold;
netty 默认的阈值 SELECTOR_AUTO_REBUILD_THRESHOLD = 512
发生空轮询
- 判断执行时间超过等待执行时间的时候,表示没有发生空轮询
- 如果等待时间小于当前时间,且 selectCnt >= 512 时,就有可能发生了空轮询
for (; ; ) {
// 省略部分代码...
int selectedKeys = selector.select(timeoutMillis);
// 轮询的次数,netty 认为 如果 selectCnt >= 512 就有可能发生了空轮询
selectCnt++;
// 省略部分代码...
if (time - TimeUnit.MILLISECONDS.toNanos(timeoutMillis) >= currentTimeNanos) {
// timeoutMillis elapsed without anything selected.
// 没有发生空轮询
selectCnt = 1;
} else if (SELECTOR_AUTO_REBUILD_THRESHOLD > 0 && selectCnt >= SELECTOR_AUTO_REBUILD_THRESHOLD) {
// The code exists in an extra method to ensure the method is not too big to inline as this
// branch is not very likely to get hit very frequently.
// 1、因为没有阻塞 timeoutMillis 的时间
// 2、selectCnt >= 512
// 就有可能发生了空轮询
// ★★★ 重建 selector 对象
selector = selectRebuildSelector(selectCnt);
selectCnt = 1;
break;
}
// 省略部分代码...
}
规避思路
- 拿到就的 selector 对象
- 遍历 selector 对象中所有的 SelectionKey,拿到感兴趣的事件和附加对象
- 取消 SelectionKey
- 将 channel 注册到新的 selector 上,并注册上感兴趣的事件和附加对象
- 返回新的 selector 对象
private void rebuildSelector0() {
final Selector oldSelector = selector;
final SelectorTuple newSelectorTuple;
if (oldSelector == null) {
return;
}
try {
// 新打开一个 selector
newSelectorTuple = openSelector();
} catch (Exception e) {
logger.warn("Failed to create a new Selector.", e);
return;
}
// Register all channels to the new Selector.
// ★ 将 oldSelector 上的有效 key 重新注册到 newSelectorTuple 上
int nChannels = 0;
for (SelectionKey key : oldSelector.keys()) {
Object a = key.attachment();
try {
// 如果 key 已经无效,就继续寻找下一个 key
if (!key.isValid() || key.channel().keyFor(newSelectorTuple.unwrappedSelector) != null) {
continue;
}
// 获取感兴趣的事件
int interestOps = key.interestOps();
// 取消旧的 key
key.cancel();
// 注册到新的 newSelectorTuple 上
SelectionKey newKey = key.channel().register(newSelectorTuple.unwrappedSelector, interestOps, a);
if (a instanceof AbstractNioChannel) {
// Update SelectionKey
((AbstractNioChannel) a).selectionKey = newKey;
}
nChannels++;
} catch (Exception e) {
logger.warn("Failed to re-register a Channel to the new Selector.", e);
if (a instanceof AbstractNioChannel) {
AbstractNioChannel ch = (AbstractNioChannel) a;
ch.unsafe().close(ch.unsafe().voidPromise());
} else {
@SuppressWarnings("unchecked")
NioTask<SelectableChannel> task = (NioTask<SelectableChannel>) a;
invokeChannelUnregistered(task, key, e);
}
}
}
// ★ 赋值为新的 selector
selector = newSelectorTuple.selector;
unwrappedSelector = newSelectorTuple.unwrappedSelector;
try {
// time to close the old selector as everything else is registered to the new one
// 关闭 oldSelector
oldSelector.close();
} catch (Throwable t) {
if (logger.isWarnEnabled()) {
logger.warn("Failed to close the old Selector.", t);
}
}
if (logger.isInfoEnabled()) {
logger.info("Migrated " + nChannels + " channel(s) to the new Selector.");
}
}