EventLoopGroup與EventLoop 源碼分析

源碼地址: GitHub

1.NioEventLoopGroup實例化過程

下面來分析對NioEventLoopGroup類進行實例化的過程中發生了什麼。

NioEventLoopGroup 類層次結構

先給出類圖:


 

我們查看上面接口的結構圖:

ExecutorService模擬Java線程池接口,ScheduledExecutorService模擬定時器線程池接口。

對於NioEventLoopGroup核心的類繼承關係就是:

NioEventLoopGroup –》MultithreadEventLoopGroup –》MultithreadEventExecutorGroup

面從這三個類出發分析NioEventLoopGroup實例化過程。

首先盜用一下網上的一張示意圖

下面大致解釋一下這個實例化過程做了什麼

//對於不指定線程數參數的構造器,默認設置0 (但是在後面的構造器中會判斷,如果設置爲0 就會初始化爲2*CPU)
    public NioEventLoopGroup() {
        this(0);
    }

    /**
     * Create a new instance using the specified number of threads, {@link ThreadFactory} and the
     * {@link SelectorProvider} which is returned by {@link SelectorProvider#provider()}.
     */
//然後調用:這裏設置了NioEventLoopGroup線程池中每個線程執行器默認是null(這裏設置爲null,在後面的構造器中會判斷,如果爲null就實例化一個線程執行器)
    public NioEventLoopGroup(int nThreads) {
        this(nThreads, (Executor) null);
    }
//再調用:這裏就存在於JDK的NIO的交互了,這裏設置了線程池的SelectorProvider, 通過SelectorProvider.provider() 返回。
    public NioEventLoopGroup(int nThreads, Executor executor) {
        this(nThreads, executor, SelectorProvider.provider());
    }
//然後調用:在這個重載的構造器中又傳入了默認的選擇策略工廠DefaultSelectStrategyFactory;
    public NioEventLoopGroup(
            int nThreads, Executor executor, final SelectorProvider selectorProvider) {
        this(nThreads, executor, selectorProvider, DefaultSelectStrategyFactory.INSTANCE);
    }
//這裏就是調用父類MultithreadEventLoopGroup的構造器了, 這裏還添加了線程的拒絕執行策略。
    public NioEventLoopGroup(int nThreads, Executor executor, final SelectorProvider selectorProvider,final SelectStrategyFactory selectStrategyFactory) {
        super(nThreads, executor, selectorProvider, selectStrategyFactory, RejectedExecutionHandlers.reject());
    }

在MultithreadEventLoopGroup構造器調用:

    protected MultithreadEventLoopGroup(int nThreads, Executor executor, Object... args) {
        super(nThreads == 0 ? DEFAULT_EVENT_LOOP_THREADS : nThreads, executor, args);
    }

接下來就是調用的基類MultithreadEventExecutorGroup的構造器:

//這個構造器裏面多傳入了一個參數 DefaultEventExecutorChooserFactory.INSTANCE ,
// 通過這個EventLoop選擇器工廠可以實例化GenericEventExecutorChooser或者PowerOfTwoEventExecutorChooser
//這個類, 這個類是EventLoopGroup線程池裏面的EventLoop的選擇器,
//調用GenericEventExecutorChooser.next() 方法可以從線程池中選擇出一個合適的EventLoop線程。    

protected MultithreadEventExecutorGroup(int nThreads, Executor executor, Object... args) {
    this(nThreads, executor, DefaultEventExecutorChooserFactory.INSTANCE, args);
}

然後就是重載調用MultithreadEventExecutorGroup類的構造器:

構造器調用到了這裏其實也就是不斷向上傳遞調用的終點了,由於構造器代碼比較長,我就刪除一些校驗和不重要的代碼,只保留核心代碼:

/**
 * 最終的創建實例構造器
 *
 * @param nThreads          該實例將使用的線程數
 * @param executor          將要使用的executor, 默認爲null
 * @param chooserFactory    將要使用的EventExecutorChooserFactory
 * @param args              arguments which will passed to each {@link #newChild(Executor, Object...)} call
 */
protected MultithreadEventExecutorGroup(int nThreads, Executor executor,
                                        EventExecutorChooserFactory chooserFactory, Object... args) {
    /** 1.初始化線程池 */
    //參數校驗nThread合法性,
    if (nThreads <= 0) {
        throw new IllegalArgumentException(String.format("nThreads: %d (expected: > 0)", nThreads));
    }
	//executor校驗非空, 如果爲空就創建ThreadPerTaskExecutor, 該類實現了 Executor接口
    //這個executor 是用來執行線程池中的所有的線程,也就是所有的NioEventLoop,其實從
    //NioEventLoop構造器中也可以知道,NioEventLoop構造器中都傳入了executor這個參數。
    if (executor == null) {
        executor = new ThreadPerTaskExecutor(newDefaultThreadFactory());
    }
	//這裏的children數組, 其實就是線程池的核心實現,線程池中就是通過指定的線程數組來實現線程池;
    //數組中每個元素其實就是一個EventLoop,EventLoop是EventExecutor的子接口。
    children = new EventExecutor[nThreads];

	//for循環實例化children數組,NioEventLoop對象
    for (int i = 0; i < nThreads; i ++) {
        boolean success = false;
        try {
        	//newChild(executor, args) 函數在NioEventLoopGroup類中實現了, 
            // 實質就是就是存入了一個 NIOEventLoop類實例
            children[i] = newChild(executor, args);
            success = true;
        } catch (Exception e) {
            // TODO: Think about if this is a good exception type
            throw new IllegalStateException("failed to create a child event loop", e);
        } finally {
         	//如果構造失敗, 就清理資源
            if (!success) {
                for (int j = 0; j < i; j ++) {
                    children[j].shutdownGracefully();
                }

                for (int j = 0; j < i; j ++) {
                    EventExecutor e = children[j];
                    try {
                        while (!e.isTerminated()) {
                            e.awaitTermination(Integer.MAX_VALUE, TimeUnit.SECONDS);
                        }
                    } catch (InterruptedException interrupted) {
                        // Let the caller handle the interruption.
                        Thread.currentThread().interrupt();
                        break;
                    }
                }
            }
        }
    }
	// 2.實例化線程工廠執行器選擇器: 根據children獲取選擇器 
    chooser = chooserFactory.newChooser(children);
	// 3.爲每個EventLoop線程添加 線程終止監聽器
    final FutureListener<Object> terminationListener = new FutureListener<Object>() {
        @Override
        public void operationComplete(Future<Object> future) throws Exception {
            if (terminatedChildren.incrementAndGet() == children.length) {
                terminationFuture.setSuccess(null);
            }
        }
    };

    for (EventExecutor e: children) {
        e.terminationFuture().addListener(terminationListener);
    }
	// 4. 將children 添加到對應的set集合中去重, 表示只可讀。
    Set<EventExecutor> childrenSet = new LinkedHashSet<EventExecutor>(children.length);
    Collections.addAll(childrenSet, children);//(EventLoop是EventExecutor的子接口)
    readonlyChildren = Collections.unmodifiableSet(childrenSet);
}

總結一下上面的初始化步驟(3)中的一些重點:

1)NIOEventLoopGroup的線程池實現其實就是一個NIOEventLoop數組,一個NIOEventLoop可以理解成就是一個線程。

2)所有的NIOEventLoop線程是使用相同的 executor、SelectorProvider、SelectStrategyFactory、RejectedExecutionHandler以及是屬於某一個NIOEventLoopGroup的。 這一點從 newChild(executor, args); 方法就可以看出:newChild()的實現是在NIOEventLoopGroup中實現的。

    @Override
    protected EventLoop newChild(Executor executor, Object... args) throws Exception {
        return new NioEventLoop(this, executor, (SelectorProvider) args[0],
            ((SelectStrategyFactory) args[1]).newSelectStrategy(), (RejectedExecutionHandler) args[2]);
    }

3)當有IO事件來時,需要從線程池中選擇一個線程出來執行,這時候的NioEventLoop選擇策略是由GenericEventExecutorChooser實現的, 並調用該類的next() 方法獲取到下一個 NioEventLoop.

到了這裏線程池的初始化就已經結束了, 基本這部分就只涉及 netty線程池的內容,不涉及到channel 與 channelPipeline和ChannelHandler等內容。下面的內容就是分析 NioEventLoop的構造器實現了。

2. NioEventLoop類層次結構

NioEventLoop 的類層次結構圖還是比較複雜的, 不過我們只需要關注幾個重要的點即可. 首先 NioEventLoop 的繼承鏈如下:

NioEventLoop -> SingleThreadEventLoop -> SingleThreadEventExecutor -> AbstractScheduledEventExecutor

在 AbstractScheduledEventExecutor 中, Netty 實現了 NioEventLoop 的 schedule 功能, 即我們可以通過調用一個 NioEventLoop 實例的 schedule()方法來運行一些定時任務. 而在 SingleThreadEventLoop 中, 又實現了任務隊列的功能, 通過它, 我們可以調用一個 NioEventLoop 實例的 execute() 方法來向任務隊列中添加一個 task, 並由 NioEventLoop 進行調度執行.

通常來說, NioEventLoop 肩負着兩種任務,: 
1)第一個是作爲 IO 線程, 執行與 Channel 相關的 IO 操作, 包括 調用 select 等待就緒的 IO 事件、讀寫數據與數據的處理等;

2)而第二個任務是作爲任務隊列, 執行 taskQueue 中的任務, 例如用戶調用 eventLoop.schedule 提交的定時任務也是這個線程執行的.

NioEventLoop 的實例化過程

對於NioEventLoop的實例化,基本就是在NioEventLoopGroup.newChild() 中調用的,下面先給出源碼:

@Override
protected EventLoop newChild(Executor executor, Object... args) throws Exception {
    return new NioEventLoop(this, executor, (SelectorProvider) args[0],
        ((SelectStrategyFactory) args[1]).newSelectStrategy(), (RejectedExecutionHandler) args[2]);
}

從上面函數裏面 new NioEventLoop()出發分析實例化過程:

(1)最先調用NioEventLoop 裏面的構造函數:

    NioEventLoop(NioEventLoopGroup parent, Executor executor, SelectorProvider selectorProvider,
                 SelectStrategy strategy, RejectedExecutionHandler rejectedExecutionHandler) {
        super(parent, executor, false, DEFAULT_MAX_PENDING_TASKS, rejectedExecutionHandler);
        if (selectorProvider == null) {
            throw new NullPointerException("selectorProvider");
        }
        if (strategy == null) {
            throw new NullPointerException("selectStrategy");
        }
        provider = selectorProvider;
        final SelectorTuple selectorTuple = openSelector();
        selector = selectorTuple.selector;
        unwrappedSelector = selectorTuple.unwrappedSelector;
        selectStrategy = strategy;
    }

需要注意的是:構造器裏面傳入了 NioEventLoopGroup、Executor、SelectorProvider、SelectStrategyFactory、RejectedExecutionHandler。從這裏可以看出,一個NioEventLoop屬於某一個NioEventLoopGroup, 且處於同一個NioEventLoopGroup下的所有NioEventLoop 公用Executor、SelectorProvider、SelectStrategyFactory和RejectedExecutionHandler。

還有一點需要注意的是,這裏的SelectorProvider構造參數傳入的是通過在NioEventLoopGroup裏面的構造器裏面的 SelectorProvider.provider();方式獲取的, 而這個方法返回的是一個單例SelectorProvider, 所以所有的NioEventLoop公用同一個單例SelectorProvider。

 

(2)核心的東西說完了,就是調用父類SingleThreadEventLoop的構造器:

    protected SingleThreadEventLoop(EventLoopGroup parent, Executor executor,
                                    boolean addTaskWakesUp, int maxPendingTasks,
                                    RejectedExecutionHandler rejectedExecutionHandler) {
        //父類構造器
        super(parent, executor, addTaskWakesUp, maxPendingTasks, rejectedExecutionHandler);
        tailTasks = newTaskQueue(maxPendingTasks);
    }

這裏除了調用父類SingleThreadEventExecutor的構造器以外, 就是實例化了 tailTasks 這個變量; 
對於tailTasks在SingleThreadEventLoop屬性的定義如下:

private final Queue<Runnable> tailTasks;// 尾部任務隊列

隊列的數量maxPendingTasks參數默認是SingleThreadEventLoop.DEFAULT_MAX_PENDING_TASK,其實就是Integer.MAX_VALUE; 對於new的這個隊列, 其實就是一個LinkedBlockingQueue 無界隊列。

(3)再看調用的父類SingleThreadEventExecutor的構造器:

    protected SingleThreadEventExecutor(EventExecutorGroup parent, Executor executor,
                                        boolean addTaskWakesUp, int maxPendingTasks,
                                        RejectedExecutionHandler rejectedHandler) {
        super(parent);// 設置EventLoop所屬於的EventLoopGroup
        this.addTaskWakesUp = addTaskWakesUp;
        this.maxPendingTasks = Math.max(16, maxPendingTasks);//默認是Integer.MAX_VALUE
        this.executor = ObjectUtil.checkNotNull(executor, "executor");
        taskQueue = newTaskQueue(this.maxPendingTasks);//創建EventLoop的任務隊列, 默認是 LinkedBlockingQueue
        rejectedExecutionHandler = ObjectUtil.checkNotNull(rejectedHandler, "rejectedHandler");
    }

自此,NioEventLoop的實例化過程已經分析完畢。

3接下來分析ServerBootstrap 的引導過程

ServerBootstrap的繼承結構圖如下: ServerBootstrap中變量負責SocketChannel的生成,AbstractBootstrap負責ServerSocketChannel的生成。

服務端程序代碼如下:(不重要的部分已經省略)

public class BaseServer {
...
    public void start(){
        EventLoopGroup bossGroup = new NioEventLoopGroup();
        EventLoopGroup workerGroup = new NioEventLoopGroup();

        try {
            ServerBootstrap sbs = new ServerBootstrap()
                    .group(bossGroup,workerGroup)
                    .channel(NioServerSocketChannel.class)
                    .childHandler(new ChannelInitializer<SocketChannel>() {

                        @Override
                        protected void initChannel(SocketChannel ch) throws Exception {
                            ch.pipeline().addLast(new LineBasedFrameDecoder(1024,true,true));

                            ch.pipeline().addLast(new StringDecoder());
                            ch.pipeline().addLast(new BaseServerHandler());
                        };

                    }).option(ChannelOption.SO_BACKLOG, 128)
                    .childOption(ChannelOption.SO_KEEPALIVE, true);
            // 綁定端口,開始接收進來的連接
            ChannelFuture future = sbs.bind(port).sync();

            System.out.println("Server start listen at " + port );
            future.channel().closeFuture().sync();
        } catch (Exception e) {
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }
...
}

這裏調用時,我們傳入了兩個EventLoopGroup分別叫bossGroup和childGroup。boss這個EventLoopGroup作爲一個acceptor負責接收來自客戶端的請求,然後分發給worker這個EventLoopGroup來處理所有的時間event和channel的IO.

3.1設置EventLoopGroup對象

    public ServerBootstrap group(EventLoopGroup parentGroup, EventLoopGroup childGroup) {
        super.group(parentGroup);
        if (childGroup == null) {
            throw new NullPointerException("childGroup");
        }
        if (this.childGroup != null) {
            throw new IllegalStateException("childGroup set already");
        }
        this.childGroup = childGroup;
        return this;
    }

查看上面源碼可知,首先調用super.group() 這個方法調用ServerBootstrap的父類AbstractBootstrap的group(EventLoopGroup group) 方法,下面看看AbstractBootstrap方法的定義:

    public B group(EventLoopGroup group) {
        if (group == null) {
            throw new NullPointerException("group");
        }
        if (this.group != null) {
            throw new IllegalStateException("group set already");
        }
        this.group = group;
        return (B) this;
    }

3.2 設置ServerBootstrap的channel() : channel(NioServerSocketChannel.class)

實際上是調用的是AbstractBootstrap裏面的channel()函數,下面先粘貼出該方法的源碼:

public B channel(Class<? extends C> channelClass) {
    if (channelClass == null) {
        throw new NullPointerException("channelClass");
    }
    return channelFactory(new ReflectiveChannelFactory<C>(channelClass));
}

這裏傳入的是一個Class對象,根據傳入的不同的Class對象,實例化不同的Channel,主要是有兩種代表NIO和OIO的對象:NioServerSocketChannel和OioServerSocketChannel。在函數體的最後調用了: 

channelFactory(new ReflectiveChannelFactory<C>(channelClass));

參數裏面實例化了一個 ReflectiveChannelFactory對象,這個對象實現了ChannelFactory這個接口的。接下來進入這個函數

    public B channelFactory(ChannelFactory<? extends C> channelFactory) {
        if (channelFactory == null) {
            throw new NullPointerException("channelFactory");
        }
        if (this.channelFactory != null) {
            throw new IllegalStateException("channelFactory set already");
        }

        this.channelFactory = channelFactory;
        return (B) this;
    }

最後把new的 ReflectiveChannelFactory傳遞給了AbstractBootstrap的channelFactory屬性,該屬性定義如下:

private volatile ChannelFactory<? extends C> channelFactory;

3.3 接下來配置ServerBootstrap的childHandler(ChannelHandler childHandler)

該函數的主要作用是設置channelHandler來處理客戶端的請求的channel的IO,這裏我們一般都用ChannelInitializer這個類的實例或則繼承自這個類的實例,我們實現如下:

childHandler(new ChannelInitializer<SocketChannel>() {
    @Override
    protected void initChannel(SocketChannel ch) throws Exception {
        ch.pipeline().addLast(new LineBasedFrameDecoder(1024,true,true));

        ch.pipeline().addLast(new StringDecoder());
        ch.pipeline().addLast(new BaseServerHandler());
    };
})

我們ChannelInitializer這個類的繼承圖:

ChannelInitializer繼承自ChannelHandler。 並且這個類其實就是往pipeline中添加了很多的channelHandler,在ServerBootstrap.childHandler(ChannelHandler childHandler)中的實現如下:傳入的childHandler賦值給ServerBootstrap的childHandler屬性。

3.4 接下來配置ServerBootstrap的option(ChannelOption option , T value);

這裏調用父類AbstractBootstrap的option()方法,源碼如下:

public <T> B option(ChannelOption<T> option, T value) {
    if (option == null) {
        throw new NullPointerException("option");
    }
    if (value == null) {
        synchronized (options) {
            options.remove(option);
        }
    } else {
        synchronized (options) {
            options.put(option, value);
        }
    }
    return (B) this;
}

重要代碼是options.put(option,value); 這裏用到了options這個參數,在AbstractBootstrap的定義如下:

private final Map<ChannelOption<?>, Object> options = new LinkedHashMap<ChannelOption<?>, Object>();

可知是私有變量,而且是一個Map集合,這個變量主要是設置TCP鏈接中的一些可選項,而且這些屬性是作用於每一個連接到服務器被創建的channel.

3.5 然後調用ServerBootstrap的childOption(ChannelOption childOption,T value);源碼如下:

public <T> ServerBootstrap childOption(ChannelOption<T> childOption, T value) {
    if (childOption == null) {
        throw new NullPointerException("childOption");
    }
    if (value == null) {
        synchronized (childOptions) {
            childOptions.remove(childOption);
        }
    } else {
        synchronized (childOptions) {
            childOptions.put(childOption, value);
        }
    }
    return this;
}

這個函數和option()函數類似,唯一區別是該屬性設定只作用於被acceptor(也就是boss EventLoopGroup)接收之後的channel。

3.6 設置ServerBootstrap的綁定端口和啓動:bind(int port)

 

 

 

 

 

前面已經分析完了EventLoopGroup和EventLoop,那麼有一個問題,我們知道一個EventLoop實際上是對應於一個線程,那麼這個EventLoop是什麼時候啓動的呢?

//未完待續

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章