簡介
在netty中不管是服務器端的ServerBootstrap還是客戶端的Bootstrap,在創建的時候都需要在group方法中傳入一個EventLoopGroup參數,用來處理所有的ServerChannel和Channel中所有的IO操作和event。
可能有的小伙伴還稍微看了一下netty的源碼,可能會發現還有一個和EventLoopGroup非常類似的類叫做EventLoop。那么EventLoopGroup和EventLoop有什么關系呢?他們的底層和channel的交互關系是怎么樣的呢?一起來看看吧。
EventLoopGroup和EventLoop
EventLoopGroup繼承自EventExecutorGroup:
public interface EventLoopGroup extends EventExecutorGroup
在前面的文章中我們講過,EventExecutorGroup中有一個next方法可以返回對應的EventExecutor,這個方法在EventLoopGroup中進行了重寫:
EventLoop next();
next方法返回的不再是一個EventExecutor,而是一個EventLoop。
事實上,EventLoop和EventLoopGroup的關系與EventExecutor和EventExecutorGroup的關系有些類似,EventLoop也是繼承自EventLoopGroup,EventLoopGroup是EventLoop的集合。
public interface EventLoop extends OrderedEventExecutor, EventLoopGroup
在EventLoopGroup中,除了重寫的next方法之外,還添加了channel的注冊方法register,用於將channel和注冊到EventLoop中,從而實現channel和EventLoop的綁定。
ChannelFuture register(Channel channel);
在EventLoop中,自多添加了一個parent方法,用來表示EventLoop和EventLoopGroup的關聯關系:
EventLoopGroup parent();
EventLoopGroup在netty中的默認實現
EventLoopGroup在netty中的默認實現叫做DefaultEventLoopGroup,先來看一下它的繼承關系:

如果看了之前我講解的關於EventExecutorGroup的朋友可以看出來,DefaultEventLoopGroup和DefaultEventExecutorGroup的繼承關系是很類似的,DefaultEventLoopGroup繼承自MultithreadEventLoopGroup,而MultithreadEventLoopGroup又繼承自MultithreadEventExecutorGroup並且實現了EventLoopGroup接口:
public abstract class MultithreadEventLoopGroup extends MultithreadEventExecutorGroup implements EventLoopGroup
MultithreadEventLoopGroup是用多線程來處理Event Loop。
在MultithreadEventLoopGroup中定義了一個DEFAULT_EVENT_LOOP_THREADS來存儲默認的處理Event Loop線程的個數:
DEFAULT_EVENT_LOOP_THREADS = Math.max(1, SystemPropertyUtil.getInt(
"io.netty.eventLoopThreads", NettyRuntime.availableProcessors() * 2));
對於EventLoopGroup中新加的幾個register方法,MultithreadEventLoopGroup都是調用對應的next方法來實現的:
public ChannelFuture register(Channel channel) {
return next().register(channel);
}
這里的next()方法的實現實際上調用的是父類的next方法,也就是MultithreadEventExecutorGroup中的next方法,來選擇Group管理的一個EventLoop:
public EventLoop next() {
return (EventLoop) super.next();
}
對於DefaultEventLoopGroup來說,它繼承自MultithreadEventLoopGroup,實現了一個newChild方法,用來將傳入的executor封裝成為EventLoop:
protected EventLoop newChild(Executor executor, Object... args) throws Exception {
return new DefaultEventLoop(this, executor);
}
EventLoop在netty中的默認實現
EventLoop在netty中的默認實現叫做DefaultEventLoop,先來看下它的繼承關系:

DefaultEventLoop繼承自SingleThreadEventLoop,而SingleThreadEventLoop又繼承自SingleThreadEventExecutor並且實現了EventLoop接口。
先來看下SingleThreadEventLoop的實現:
public abstract class SingleThreadEventLoop extends SingleThreadEventExecutor implements EventLoop
SingleThreadEventLoop使用單一線程來執行提交的任務。它和SingleThreadEventExecutor相比有什么變化呢?
首先 提供了一個tailTasks用來存儲pending的tasks:
private final Queue<Runnable> tailTasks;
這個tailTasks會被用在任務個數的判斷和操作上:
final boolean removeAfterEventLoopIterationTask(Runnable task) {
return tailTasks.remove(ObjectUtil.checkNotNull(task, "task"));
}
protected boolean hasTasks() {
return super.hasTasks() || !tailTasks.isEmpty();
}
public int pendingTasks() {
return super.pendingTasks() + tailTasks.size();
}
SingleThreadEventLoop中對register方法的實現最終調用的是注冊的channel中unsafe的register方法:
channel.unsafe().register(this, promise);
再來看一下DefaultEventLoop,DefaultEventLoop繼承自SingleThreadEventLoop:
public class DefaultEventLoop extends SingleThreadEventLoop
除了構造函數之外,DefaultEventLoop實現了一個run方法,用來具體任務的執行邏輯:
protected void run() {
for (;;) {
Runnable task = takeTask();
if (task != null) {
task.run();
updateLastExecutionTime();
}
if (confirmShutdown()) {
break;
}
}
}
如果對比可以發現,DefaultEventLoop和DefaultEventExecutor中run方法的實現是一樣的。
總結
本文介紹了netty中EventLoop和EventLoopGroup的默認實現:DefaultEventLoop和DefaultEventLoopGroup,但是不知道小伙伴們有沒有發現,即使在最簡單的netty應用中也很少看到這兩個默認的EventLoop。最常用的反而是NioEventLoopGroup和NioEventLoop,這是因為DefaultEventLoop和DefaultEventLoopGroup只是使用了多線程技術,一個線程代表一個EventLoop,在EventLoop過多的情況下可能會造成線程和性能的浪費,所以在NioEventLoopGroup和NioEventLoop使用了NIO技術,通過使用channel、selector等NIO技術提升了EventLoop的效率。關於NioEventLoopGroup和NioEventLoop的詳細介紹,我們會在后一章中詳細講解,敬請期待。
本文已收錄於 http://www.flydean.com/05-1-netty-eventloop-eventloopgroup/
最通俗的解讀,最深刻的干貨,最簡潔的教程,眾多你不知道的小技巧等你來發現!
歡迎關注我的公眾號:「程序那些事」,懂技術,更懂你!