我们知道 netty的事件循环组其实就是线程组,每一个线程对象都是一个NioEventLoop对象,NioEventLoop实现了Executor接口,所以我们关注NioEventLoop时 最主要关注的它的run方法
查看继承关系图
在父类io.netty.util.concurrent.SingleThreadEventExecutor#execute(java.lang.Runnable)中重写了execute方法 也很重要
public void execute(Runnable task) {//重写了execute方法
ObjectUtil.checkNotNull(task, "task");
execute(task, !(task instanceof LazyRunnable) && wakesUpForTask(task));
}
/**
* netty重写了execute方法
* 干了几件事件
* 1、addTask将Runnable对象添加到任务队列taskQueue中
* 2、startThread启动该NioEventLoop线程 自旋并处理io任务
* 3、Selector.wakeUp唤醒 select(...)阻塞的worker线程 告诉它有新的感兴趣的事件注册过来了 别睡了要开始干活了
*/
private void execute(Runnable task, boolean immediate) {
boolean inEventLoop = inEventLoop();
addTask(task);//添加任务
if (!inEventLoop) {//2中情况 主线程启动bossGroup线程 bossGroup启动workGroup线程
startThread();//启动线程
if (isShutdown()) {
boolean reject = false;
try {
if (removeTask(task)) {
reject = true;
}
} catch (UnsupportedOperationException e) {
...
}
if (reject) {
reject();//拒绝
}
}
}
if (!addTaskWakesUp && immediate) {
wakeup(inEventLoop);//唤醒阻塞中的Selector
}
}
protected void addTask(Runnable task) {
ObjectUtil.checkNotNull(task, "task");
if (!offerTask(task)) {//入队
reject(task);
}
}
final boolean offerTask(Runnable task) {
if (isShutdown()) {
reject();
}
return taskQueue.offer(task);
}
private void startThread() {
//通过cas操作保证该线程只会启动一次
if (state == ST_NOT_STARTED) {//判断线程开启状态 是否为还未开启ST_NOT_STARTED
if (STATE_UPDATER.compareAndSet(this, ST_NOT_STARTED, ST_STARTED)) {//cas 成ST_STARTED
boolean success = false;
try {
doStartThread();//真正开启线程
success = true;
} finally {
if (!success) {//如果线程开启失败
STATE_UPDATER.compareAndSet(this, ST_STARTED, ST_NOT_STARTED);//cas 成ST_NOT_STARTED
}
}
}
}
}
private void doStartThread() {
assert thread == null;
executor.execute(new Runnable() {//执行ThreadPerTaskExecutor的start方法 start启动线程
@Override
public void run() {
thread = Thread.currentThread();
if (interrupted) {
thread.interrupt();
}
boolean success = false;
updateLastExecutionTime();
try {
SingleThreadEventExecutor.this.run();//执行自行NioEventLoop的run方法 线程组的线程自旋起来
success = true;
} catch (Throwable t) {
logger.warn("Unexpected exception from an event executor: ", t);
} finally {
//线程执行完之后关闭的事情
...
...
...
}
}
});
}
netty重写了execute方法 干了几件事件 1、addTask将Runnable对象添加到任务队列taskQueue中 2、startThread启动该NioEventLoop线程 自旋并处理io任务 3、Selector.wakeUp唤醒 select(...)阻塞的worker线程 告诉它有新的感兴趣的事件注册过来了 让worker线程别睡了要开始干活了
我们看到 启动线程时是调用的io.netty.channel.nio.NioEventLoop#run方法
继续分析run方法
/**
* run方法大致干的事情
* 1、每一个NioEventLoop都是一个线程 都会执行自己的run方法 包括bossGroup和workerGroup
* 2、线程是自旋阻塞的,通过selector.select(timeoutMillis)来阻塞的 唤醒的方式有两种
* 2.1 bossGroup接收了新的客户端 将SocketChannel注册到workerGroup的selector之上时 通过selector.wakeUp唤醒
* 2.2 selector上感兴趣的事件就绪了 有返回值 也会唤醒
* 3、processSelectedKeys 处理Selector的就绪通道
* 4、runAllTasks 处理taskQueue存放的队列任务
*/
@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:
// fall-through to SELECT since the busy-wait is not supported with NIO
case SelectStrategy.SELECT://任务队列无数据 返回-1 走这块逻辑
long curDeadlineNanos = nextScheduledTaskDeadlineNanos();//获取下一个任务的任务期限事件 也就是阻塞时间
if (curDeadlineNanos == -1L) {
curDeadlineNanos = NONE; // nothing on the calendar
}
nextWakeupNanos.set(curDeadlineNanos);
try {
if (!hasTasks()) {
strategy = select(curDeadlineNanos);//阻塞一个相对比较长的事件 等待有其他事件被唤醒
}
} finally {
// This update is just to help block unnecessary selector wakeups
// so use of lazySet is ok (no race condition)
nextWakeupNanos.lazySet(AWAKE);
}
// fall through
default:
}
} catch (IOException e) {
// If we receive an IOException here its because the Selector is messed up. Let's rebuild
// the selector and retry. https://github.com/netty/netty/issues/8566
rebuildSelector0();
selectCnt = 0;
handleLoopException(e);
continue;
}
selectCnt++;
cancelledKeys = 0;
needsToSelectAgain = false;
final int ioRatio = this.ioRatio;
boolean ranTasks;
if (ioRatio == 100) {
try {
if (strategy > 0) {
processSelectedKeys();//处理Selector有响应的事件
}
} finally {
// Ensure we always run tasks.
ranTasks = runAllTasks();//处理taskQueue存放的队列任务
}
} else if (strategy > 0) {
final long ioStartTime = System.nanoTime();
try {
processSelectedKeys();//处理Selector有响应的事件
} finally {
// Ensure we always run tasks.
final long ioTime = System.nanoTime() - ioStartTime;
ranTasks = runAllTasks(ioTime * (100 - ioRatio) / ioRatio);//处理taskQueue存放的队列任务
}
} else {
ranTasks = runAllTasks(0); // This will run the minimum number of tasks
}
if (ranTasks || strategy > 0) {
if (selectCnt > MIN_PREMATURE_SELECTOR_RETURNS && logger.isDebugEnabled()) {
logger.debug("Selector.select() returned prematurely {} times in a row for Selector {}.",
selectCnt - 1, selector);
}
selectCnt = 0;
} else if (unexpectedSelectorWakeup(selectCnt)) { // Unexpected wakeup (unusual case)
selectCnt = 0;
}
} catch (CancelledKeyException e) {
// Harmless exception - log anyway
if (logger.isDebugEnabled()) {
logger.debug(CancelledKeyException.class.getSimpleName() + " raised by a Selector {} - JDK bug?",
selector, e);
}
} catch (Error e) {
throw (Error) e;
} catch (Throwable t) {
handleLoopException(t);
} finally {
// Always handle shutdown even if the loop processing threw an exception.
try {
if (isShuttingDown()) {
closeAll();
if (confirmShutdown()) {
return;
}
}
} catch (Error e) {
throw (Error) e;
} catch (Throwable t) {
handleLoopException(t);
}
}
}
}
private void processSelectedKeys() {
if (selectedKeys != null) {
processSelectedKeysOptimized();//有就绪事件 走该方法
} else {
processSelectedKeysPlain(selector.selectedKeys());
}
}
private void processSelectedKeysOptimized() {
for (int i = 0; i < selectedKeys.size; ++i) {
final SelectionKey k = selectedKeys.keys[i];
selectedKeys.keys[i] = null;
final Object a = k.attachment();//拿到NioServerSocketChannel或者NioSocketChannel
if (a instanceof AbstractNioChannel) {
processSelectedKey(k, (AbstractNioChannel) a);//处理select事件
} 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()) {//key失效
...
return;
}
try {
int readyOps = k.readyOps();
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();
}
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
ch.unsafe().forceFlush();//强制刷新
}
if ((readyOps & (SelectionKey.OP_READ | SelectionKey.OP_ACCEPT)) != 0 || readyOps == 0) {//读事件或者 接收事件
unsafe.read();//读事件 bossGroup和workerGroup不一样 参考前文的unafe分析
}
} catch (CancelledKeyException ignored) {
unsafe.close(unsafe.voidPromise());
}
}
protected boolean runAllTasks() {//处理任务队列中所有的任务
assert inEventLoop();
boolean fetchedAll;
boolean ranAtLeastOne = false;
do {
fetchedAll = fetchFromScheduledTaskQueue();
if (runAllTasksFrom(taskQueue)) {//处理taskQueue中所有的任务
ranAtLeastOne = true;
}
} while (!fetchedAll); // keep on processing until we fetched all scheduled tasks.
if (ranAtLeastOne) {
lastExecutionTime = ScheduledFutureTask.nanoTime();
}
afterRunningAllTasks();
return ranAtLeastOne;
}
protected final boolean runAllTasksFrom(Queue<Runnable> taskQueue) {
Runnable task = pollTaskFrom(taskQueue);//从任务队列中poll获取task
if (task == null) {
return false;
}
for (;;) {
safeExecute(task);//安全执行
task = pollTaskFrom(taskQueue);
if (task == null) {//
return true;
}
}
}
protected static void safeExecute(Runnable task) {
try {
task.run();//就是调用task的run方法
} catch (Throwable t) {
logger.warn("A task raised an exception. Task: {}", task, t);
}
}
总结run方法:
1、每一个NioEventLoop都是一个线程 都会执行自己的run方法 包括bossGroup和workerGroup
2、线程是自旋阻塞的,通过selector.select(timeoutMillis)来阻塞的 唤醒的方式有两种
2.1 bossGroup接收了新的客户端 将SocketChannel注册到workerGroup的selector之上时 通过selector.wakeUp唤醒
2.2 selector上感兴趣的事件就绪了 有返回值 也会唤醒
3、processSelectedKeys 处理Selector的就绪通道
4、runAllTasks 处理taskQueue存放的队列任务