netty系列之:EventLoop,EventLoopGroup和netty的預設實現

flydean發表於2022-03-07

簡介

在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,先來看一下它的繼承關係:

<img src="https://img-blog.csdnimg.cn/119283e9b8d04854940abc0fc159c604.png" style="zoom:67%;" />

如果看了之前我講解的關於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,先來看下它的繼承關係:

<img src="https://img-blog.csdnimg.cn/9c642b58f6c248f9bddfbb71799549f9.png" style="zoom:67%;" />

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/

最通俗的解讀,最深刻的乾貨,最簡潔的教程,眾多你不知道的小技巧等你來發現!

歡迎關注我的公眾號:「程式那些事」,懂技術,更懂你!

相關文章