上一節,我們研究了NioEventLoop的創建過程,其實也就是做了一些初始化,把該準備的準備好了。重點有兩個
- 準備了一個
ThreadPerTaskExecutor
,以後添加的task
,每次執行的時候其實是實例化了了一個netty自定義的Thread :(FastThreadLocalThread
)然後再調用start()
執行任務。 - 準備了一個
NioEventLoop
數組(EventExecutor
),配套了一個循環使用該數組的選擇器chooser
,該選擇器根據"線程數是否是2的冪次方"提供了不同選擇策略。 - 準備了
taskQueue
和tailQueue
用於存放eventLoop
任務,和外部線程任務
這一節,具體研究NioEventLoop
的的啓動過程。
入口: 端口綁定的時候AbstractBootstrap#doBind0()
//入口
ChannelFuture future = serverBootstrap.bind(PORT).sync();
doBind(localAddress)
doBind0(regFuture, channel, localAddress, promise);
private static void doBind0(
final ChannelFuture regFuture, final Channel channel,
final SocketAddress localAddress, final ChannelPromise promise) {
// This method is invoked before channelRegistered() is triggered. Give user handlers a chance to set up
// the pipeline in its channelRegistered() implementation.
channel.eventLoop().execute(new Runnable() {
@Override
public void run() {
if (regFuture.isSuccess()) {
channel.bind(localAddress, promise).addListener(ChannelFutureListener.CLOSE_ON_FAILURE);
} else {
promise.setFailure(regFuture.cause());
}
}
});
}
首先是channel.eventLoop()
獲取的肯定是NioEventLoop
,但是具體是怎麼獲取的呢?往下研究
@Override
public NioEventLoop eventLoop() {
return (NioEventLoop) super.eventLoop();
}
@Override
public EventLoop eventLoop() {
EventLoop eventLoop = this.eventLoop;
if (eventLoop == null) {
throw new IllegalStateException("channel not registered to an event loop");
}
return eventLoop;
}
問題來了。該方法確實是獲取的NioEventLoop
,但是是什麼時候初始化AbstractChannel#eventLoop
這個成員變量的呢?翻看之前的《註冊Selector》 過程的分析,發現確實是註冊的時候做的。也就是AbstractChannel.AbstractUnsafe#register()
方法中,通過方法參數講eventLoop
傳入,以下這段代碼
@Override
public final void register(EventLoop eventLoop, final ChannelPromise promise) {
//省略代碼
AbstractChannel.this.eventLoop = eventLoop;
//省略代碼
}
該方法被SingleThreadEventLoop#register()
調用並將自己傳入的,其本身就是NioEventLoop
的父類,在創建的時候就被存儲子啊EventExecutor[]
數組中了。那麼是怎麼從數組中取出來的呢?
@Override
public ChannelFuture register(final ChannelPromise promise) {
ObjectUtil.checkNotNull(promise, "promise");
promise.channel().unsafe().register(this, promise);
return promise;
}
前面也提到過,註冊的過程我們發現從EventExecutor[]
中選擇EventLoop
的代碼是在MultithreadEventLoopGroup#register()
中
@Override
public ChannelFuture register(Channel channel) {
return next().register(channel);
}
@Override
public EventLoop next() {
return (EventLoop) super.next();
}
//io.netty.util.concurrent.MultithreadEventExecutorGroup#next()
@Override
public EventExecutor next() {
return chooser.next();
}
@UnstableApi
interface EventExecutorChooser {
/**
* Returns the new {@link EventExecutor} to use.
*/
EventExecutor next();
}
終於,在這裏獲取了之前創建NioEventLoop準備好的選擇器。那麼next()
方法是在兩個選擇器策略裏面定義的,用於繼續按下標後,循環獲取EventExecutor[]
數組中的EventLoop
(這裏是NioEventLoop
)
private static final class PowerOfTwoEventExecutorChooser implements EventExecutorChooser {
private final AtomicInteger idx = new AtomicInteger();
private final EventExecutor[] executors;
PowerOfTwoEventExecutorChooser(EventExecutor[] executors) {
this.executors = executors;
}
@Override
public EventExecutor next() {
return executors[idx.getAndIncrement() & executors.length - 1];
}
}
private static final class GenericEventExecutorChooser implements EventExecutorChooser {
private final AtomicInteger idx = new AtomicInteger();
private final EventExecutor[] executors;
GenericEventExecutorChooser(EventExecutor[] executors) {
this.executors = executors;
}
@Override
public EventExecutor next() {
return executors[Math.abs(idx.getAndIncrement() % executors.length)];
}
}
回過到啓動過程,NioEventLoop
已經獲取,調用execute()
方法,並且實例化了一個Runnable
,該Runnable就是一個新的task
channel.eventLoop().execute(new Runnable() {
@Override
public void run() {
if (regFuture.isSuccess()) {
channel.bind(localAddress, promise).addListener(ChannelFutureListener.CLOSE_ON_FAILURE);
} else {
promise.setFailure(regFuture.cause());
}
}
});
而execute()
方法其實是在NioEventLoop
的父類,SingleThreadEventExecutor#execute()
。這裏有三個動作。
-
inEventLoop()
:判斷當前線程是否是EventLoop持有的線程 -
startThread()
:開啓自定義線程 -
addTask(task)
:添加到任務隊列
@Override
public void execute(Runnable task) {
if (task == null) {
throw new NullPointerException("task");
}
//判斷當前線程是否是EventLoop持有的線程,這個時間點返回的是false
boolean inEventLoop = inEventLoop();
if (inEventLoop) {
addTask(task);
} else {
//不在同一線程,開啓自定義線程
startThread();
//添加任務隊列
addTask(task);
if (isShutdown() && removeTask(task)) {
reject();
}
}
if (!addTaskWakesUp && wakesUpForTask(task)) {
wakeup(inEventLoop);
}
}
-
inEventLoop()
很多地方都用到,值得深究,追綜到SingleThreadEventExecutor#inEventLoop()
,確實是跟持有的成員變量private volatile Thread thread;
進行對比。注意,該成員變量this.thread
此時並沒有被初始化,因此爲null,返回的是false
。可以Debug進行跟蹤。
@Override
public boolean inEventLoop() {
//傳入當前線程,當前線程爲:main 線程
return inEventLoop(Thread.currentThread());
}
@Override
public boolean inEventLoop(Thread thread) {
return thread == this.thread;
}
-
startThread()
開啓一個線程用於執行NioEventLoop
的run
方法,並且讓NioEventLoop
持有這個線程。這一刻,NioEventLoop
跑起來了。
private void startThread() {
//判斷當前線程是否是未啓動的
if (STATE_UPDATER.get(this) == ST_NOT_STARTED) {
if (STATE_UPDATER.compareAndSet(this, ST_NOT_STARTED, ST_STARTED)) {
//啓動
doStartThread();
}
}
}
private void doStartThread() {
assert thread == null;
//ThreadPerTaskExecutor#execute() 開啓netty自定義的線程FastThreadLocalThread,並執行
executor.execute(new Runnable() {
@Override
public void run() {
//運行時,開始持有運行時線程
thread = Thread.currentThread();
//省略代碼
//執行NioEventLoop.run()
SingleThreadEventExecutor.this.run();
//省略代碼
});
}
-
addTask(task)
是在NioEventLoop
的線程跑起來了之後,將最初綁定端口的任務offer()
到了taskQueue
,存儲起來,異步執行。
protected void addTask(Runnable task) {
if (task == null) {
throw new NullPointerException("task");
}
if (!offerTask(task)) {
reject(task);
}
}
final boolean offerTask(Runnable task) {
if (isShutdown()) {
reject();
}
return taskQueue.offer(task);
}