簡介
在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/
最通俗的解讀,最深刻的乾貨,最簡潔的教程,眾多你不知道的小技巧等你來發現!
歡迎關注我的公眾號:「程式那些事」,懂技術,更懂你!