Netty事件循環(huán)主邏輯NioEventLoop的run方法分析
Netty事件循環(huán)主邏輯
Netty 事件循環(huán)主邏輯在 NioEventLoop.run 中的 processSelectedKeys函數(shù)中
protected void run() {
//主循環(huán)不斷讀取IO事件和task,因為 EventLoop 也是 juc 的 ScheduledExecutorService 實現(xiàn)
for (;;) {
try {
switch (selectStrategy.calculateStrategy(selectNowSupplier, hasTasks())) {
case SelectStrategy.CONTINUE:
continue;
case SelectStrategy.SELECT:
select(wakenUp.getAndSet(false));
if (wakenUp.get()) {
selector.wakeup();
}
// fall through
default:
}
cancelledKeys = 0;
needsToSelectAgain = false;
// IO事件占總執(zhí)行時間的百分比 */
final int ioRatio = this.ioRatio;
if (ioRatio == 100) {
try {
processSelectedKeys();
} finally {
// Ensure we always run tasks.
runAllTasks();
}
} else {
final long ioStartTime = System.nanoTime();
try {
processSelectedKeys();
} finally {
// Ensure we always run tasks.
final long ioTime = System.nanoTime() - ioStartTime;
runAllTasks(ioTime * (100 - ioRatio) / ioRatio);
}
}
} catch (Throwable t) {
handleLoopException(t);
}
// Always handle shutdown even if the loop processing threw an exception.
try {
if (isShuttingDown()) {
closeAll();
if (confirmShutdown()) {
return;
}
}
} catch (Throwable t) {
handleLoopException(t);
}
}
}processSelectedKeys 函數(shù) 執(zhí)行時會判斷是否執(zhí)行優(yōu)化的版本,即判斷 SelectedSelectionKeySet 是否為空。
是否開啟優(yōu)化取決于是否設(shè)置了環(huán)境變量 io.netty.noKeySetOptimization ,默認是 false 代表開啟
private static final boolean DISABLE_KEYSET_OPTIMIZATION =
SystemPropertyUtil.getBoolean("io.netty.noKeySetOptimization", false);原理是通過反射的方式設(shè)置 eventLoop綁定的selector中的 selectKeys屬性 為 SelectedSelectionKeySet ,好處是不用 迭代 selector.selectedKeys()
初始化 EventLoop
注入時機為初始化 EventLoop 的時候
private SelectorTuple openSelector() {
12 //注入邏輯40
Object maybeException = AccessController.doPrivileged(new PrivilegedAction<Object>() {
@Override
public Object run() {
try {
Field selectedKeysField = selectorImplClass.getDeclaredField("selectedKeys");
Field publicSelectedKeysField = selectorImplClass.getDeclaredField("publicSelectedKeys");
Throwable cause = ReflectionUtil.trySetAccessible(selectedKeysField);
if (cause != null) {
return cause;
}
cause = ReflectionUtil.trySetAccessible(publicSelectedKeysField);
if (cause != null) {
return cause;
}
selectedKeysField.set(unwrappedSelector, selectedKeySet);
publicSelectedKeysField.set(unwrappedSelector, selectedKeySet);
return null;
} catch (NoSuchFieldException e) {
return e;
} catch (IllegalAccessException e) {
return e;
}
}
});
........78 }處理讀事件
處理讀事件主要在processSelectedKey 中 ,分別對 讀、寫、連接事件進行了處理。
private void processSelectedKeysOptimized() {
for (int i = 0; i < selectedKeys.size; ++i) {
final SelectionKey k = selectedKeys.keys[i];
// null out entry in the array to allow to have it GC'ed once the Channel close
// See https://github.com/netty/netty/issues/2363
selectedKeys.keys[i] = null;
final Object a = k.attachment();
if (a instanceof AbstractNioChannel) {
//分別處理每個channel的事件
processSelectedKey(k, (AbstractNioChannel) a);
} else {
@SuppressWarnings("unchecked")
NioTask<SelectableChannel> task = (NioTask<SelectableChannel>) a;
processSelectedKey(k, task);
}
if (needsToSelectAgain) {
// null out entries in the array to allow to have it GC'ed once the Channel close
// See https://github.com/netty/netty/issues/2363
selectedKeys.reset(i + 1);
selectAgain();
i = -1;
}
}
}
private void processSelectedKey(SelectionKey k, AbstractNioChannel ch) {
final AbstractNioChannel.NioUnsafe unsafe = ch.unsafe();
if (!k.isValid()) {
final EventLoop eventLoop;
try {
eventLoop = ch.eventLoop();
} catch (Throwable ignored) {
// If the channel implementation throws an exception because there is no event loop, we ignore this
// because we are only trying to determine if ch is registered to this event loop and thus has authority
// to close ch.
return;
}
// Only close ch if ch is still registered to this EventLoop. ch could have deregistered from the event loop
// and thus the SelectionKey could be cancelled as part of the deregistration process, but the channel is
// still healthy and should not be closed.
// See https://github.com/netty/netty/issues/5125
if (eventLoop != this || eventLoop == null) {
return;
}
// close the channel if the key is not valid anymore
unsafe.close(unsafe.voidPromise());
return;
}
try {
int readyOps = k.readyOps();
// We first need to call finishConnect() before try to trigger a read(...) or write(...) as otherwise
// the NIO JDK channel implementation may throw a NotYetConnectedException.
if ((readyOps & SelectionKey.OP_CONNECT) != 0) {
// remove OP_CONNECT as otherwise Selector.select(..) will always return without blocking
// See https://github.com/netty/netty/issues/924
int ops = k.interestOps();
ops &= ~SelectionKey.OP_CONNECT;
k.interestOps(ops);
//處理了連接事件
unsafe.finishConnect();
}
// Process OP_WRITE first as we may be able to write some queued buffers and so free memory.
if ((readyOps & SelectionKey.OP_WRITE) != 0) {
// Call forceFlush which will also take care of clear the OP_WRITE once there is nothing left to write
//將要寫入的buffer flush掉
ch.unsafe().forceFlush();
}
// Also check for readOps of 0 to workaround possible JDK bug which may otherwise lead
// to a spin loop
if ((readyOps & (SelectionKey.OP_READ | SelectionKey.OP_ACCEPT)) != 0 || readyOps == 0) {
//回調(diào) pipeline 上所有的 ChannelInboundHandler 的 fireChannelRead 和 channelReadComplete 函數(shù)
unsafe.read();
}
} catch (CancelledKeyException ignored) {
unsafe.close(unsafe.voidPromise());
}
}注意
NioServerSocketChannel 和 NioSocketChannel 都是 同樣的 處理邏輯, 不同的是 前者 只關(guān)注 OP_ACCEPT 和 OP_READ事件, 后者 關(guān)注 OP_READ、OP_WRITE、OP_CONNECT事件
當NioServerSocketChannel 發(fā)生 OP_ACCEPT事件時 會 觸發(fā)
AbstractNioChannel.NioUnsafe.read -> NioSctpServerChannel.doReadMessages(List<Object>) -> ServerBootstrapAcceptor.channelRead ,
將受到的 NioSocketChannel 注冊到 childEventLoop 。
以上就是Netty事件循環(huán)主邏輯NioEventLoop的run方法分析的詳細內(nèi)容,更多關(guān)于Netty循環(huán)邏輯NioEventLoop run方法的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
springboot整合redis過期key監(jiān)聽實現(xiàn)訂單過期的項目實踐
現(xiàn)在各種電商平臺都有自己的訂單過期時間設(shè)置,那么如何設(shè)置訂單時間過期呢,本文主要介紹了springboot整合redis過期key監(jiān)聽實現(xiàn)訂單過期的項目實踐,感興趣的可以了解一下2023-12-12
Spring Boot如何使用httpcomponents實現(xiàn)http請求
這篇文章主要介紹了Spring Boot使用httpcomponents實現(xiàn)http請求的示例代碼,本文通過實例代碼給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2023-07-07
com.mysql.jdbc.Driver 和 com.mysql.cj.jdbc.Driver的區(qū)
這篇文章主要介紹了com.mysql.jdbc.Driver 和 com.mysql.cj.jdbc.Driver的區(qū)別以及設(shè)定serverTimezone的方法,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2023-09-09
使用Mybatis-plus清空表數(shù)據(jù)的操作方法
MyBatis 是一個基于 java 的持久層框架,它內(nèi)部封裝了 jdbc,極大提高了我們的開發(fā)效率,文中給大家介紹了MybatisPlus常用API-增刪改查功能,感興趣的朋友跟隨小編一起看看吧2022-11-11
SpringBoot使用SchedulingConfigurer實現(xiàn)多個定時任務(wù)多機器部署問題(推薦)
這篇文章主要介紹了SpringBoot使用SchedulingConfigurer實現(xiàn)多個定時任務(wù)多機器部署問題,定時任務(wù)多機器部署解決方案,方式一拆分,單獨拆分出來,單獨跑一個應(yīng)用,方式二是基于aop攔截處理(搶占執(zhí)行),只要有一個執(zhí)行,其它都不執(zhí)行,需要的朋友可以參考下2023-01-01

