56. Netty源代碼分析-服務器初始化 NioEventLoopGroup實例化

一. 代碼下載

Netty代碼下載和編譯參考前一篇Netty文章
http://blog.51cto.com/483181/2112163

二. 服務器代碼分析

2.1 服務器代碼編寫

一般Netty服務器端這樣編寫

    EventLoopGroup bossGroup = new NioEventLoopGroup();   //1. 實例化NioEventLoopGroup對象
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        try {
            ServerBootstrap b = new ServerBootstrap(); //2. 
            b.group(bossGroup, workerGroup) //3. 
                .channel(NioServerSocketChannel.class)
                .option(ChannelOption.SO_BACKLOG, 100)
                .handler(new LoggingHandler(LogLevel.INFO))
                .childHandler(new ChannelInitializer<SocketChannel>() {
                    @Override
                    protected void initChannel(SocketChannel ch) throws Exception {
                        ch.pipeline().addLast(new FixedLengthFrameDecoder(20));
                    }
                });

            ChannelFuture f = b.bind(port).sync(); //4.
            f.channel().closeFuture().sync();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }

2.2 NioEventLoopGroup

2.2.1 NioEventLoopGroup繼承關係

一步步來看,首先看第一個註釋,初始化NioEventLoopGroup對象

EventLoopGroup bossGroup = new NioEventLoopGroup();   //1. 實例化NioEventLoopGroup對象

下圖是NioEventLoopGroup的類繼承圖,包含類成員和方法,比較詳細。 這個功能是IntelliJ 自帶的。
右擊NioEventLoopGroup類名,選擇Diagrams->Show Diagram->上面有f,m的按鈕,分別對應field和method。

如下:

56. Netty源代碼分析-服務器初始化 NioEventLoopGroup實例化

2.2.2 NioEventLoopGroup構造函數

public NioEventLoopGroup() {
        this(0);
    }

        public NioEventLoopGroup(int nThreads) {
        this(nThreads, (Executor) null);
    }

public NioEventLoopGroup(int nThreads, Executor executor) {
        this(nThreads, executor, SelectorProvider.provider());
    }       

        public NioEventLoopGroup(
            int nThreads, Executor executor, final SelectorProvider selectorProvider) {
        this(nThreads, executor, selectorProvider, DefaultSelectStrategyFactory.INSTANCE);
    }

    public NioEventLoopGroup(int nThreads, Executor executor, final SelectorProvider selectorProvider,
                             final SelectStrategyFactory selectStrategyFactory) {
        super(nThreads, executor, selectorProvider, selectStrategyFactory, RejectedExecutionHandlers.reject());
    }

我們可以看到幾點

  1. NioEventLoopGroup採用的是構造函數重載的方式,以適應不同的初始化場景
  2. Executor傳的是null
  3. SelectorProvider用的是SelectorProvider.provider()
  4. 然後把構造好的參數都傳給父類MultithreadEventLoopGroup (繼承關係可以看上圖)

2.2.3 SelectorProvider.provider()

private static SelectorProvider provider = null;

public static SelectorProvider provider() {
        synchronized (lock) {
            if (provider != null)
                return provider;
            return AccessController.doPrivileged(
                new PrivilegedAction<SelectorProvider>() {
                    public SelectorProvider run() {
                            if (loadProviderFromProperty())
                                return provider;
                            if (loadProviderAsService())
                                return provider;
                            provider = sun.nio.ch.DefaultSelectorProvider.create();
                            return provider;
                        }
                    });
        }
    }

public class DefaultSelectorProvider {
    private DefaultSelectorProvider() {
    }

    public static SelectorProvider create() {
        return new KQueueSelectorProvider();
    }
}

public class KQueueSelectorProvider extends SelectorProviderImpl {
    public KQueueSelectorProvider() {
    }

    public AbstractSelector openSelector() throws IOException {
        return new KQueueSelectorImpl(this);
    }
}

這段代碼我們也可以看到幾點:

  1. SelectorProvider provider是一個單例,static類型
  2. SelectorProvider.provider的實現,產生了一個KQueueSelectorProvider
  3. KQueueSelectorProvider的openSelector會生成一個KQueueSelectorImpl

這個先記下來,也許後面分析會有用,繼續分析MultithreadEventLoopGroup的構造函數。

2.2.4 MultithreadEventLoopGroup

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

private static final int DEFAULT_EVENT_LOOP_THREADS;

    static {
        DEFAULT_EVENT_LOOP_THREADS = Math.max(1, SystemPropertyUtil.getInt(
                "io.netty.eventLoopThreads", NettyRuntime.availableProcessors() * 2));
    }       

上面這段代碼我們可以看到這幾點:

  1. 如果我們實例化NioEventLoopGroup沒有傳入參數,也就是沒有nThreads,那麼就會採用默認的DEFAULT_EVENT_LOOP_THREADS
    DEFAULT_EVENT_LOOP_THREADS如果沒有配置io.netty.eventLoopThreads的話,一般是cpu核數*2
  2. MultithreadEventLoopGroup的實例化方法是繼續調用父類的初始化方法。

繼續父類MultithreadEventExecutorGroup

2.2.5 MultithreadEventExecutorGroup

protected MultithreadEventExecutorGroup(int nThreads, Executor executor,
                                            EventExecutorChooserFactory chooserFactory, Object... args) {
        ...

        children = new EventExecutor[nThreads]; //1. 實例化children數組

        for (int i = 0; i < nThreads; i ++) { //2. 循環初始化children
            boolean success = false;
            try {
                children[i] = newChild(executor, args);
                success = true;
            } catch (Exception e) {
                throw new IllegalStateException("failed to create a child event loop", e);
            } finally {
                ...
            }
        }

        chooser = chooserFactory.newChooser(children); //3. 實例化chooser

        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);
        }

        Set<EventExecutor> childrenSet = new LinkedHashSet<EventExecutor>(children.length);
        Collections.addAll(childrenSet, children);
        readonlyChildren = Collections.unmodifiableSet(childrenSet);
    }

上面這段代碼可以從下面幾個點分析:

private final EventExecutor[] children;
  1. children - EventExecutor數組,大小是nThreads,線程數目。
  2. newChild初始化
    實例類是NioEventLoopGroup.java,返回NioEventLoop對象
    protected EventLoop newChild(Executor executor, Object... args) throws Exception {
        return new NioEventLoop(this, executor, (SelectorProvider) args[0],
            ((SelectStrategyFactory) args[1]).newSelectStrategy(), (RejectedExecutionHandler) args[2]);
    }

    NioEventLoop的繼承關係是這樣的,繼承於SingleThreadEventLoop,別忘了上面我們看到NioEventLoopGroup繼承自MultithreadEventLoopGroup.(看名字是單線程和多線程的區別?)

56. Netty源代碼分析-服務器初始化 NioEventLoopGroup實例化

繼續看NioEventLoop的構造函數

2.2.6 NioEventLoop

NioEventLoop(NioEventLoopGroup parent, Executor executor, SelectorProvider selectorProvider,
                 SelectStrategy strategy, RejectedExecutionHandler rejectedExecutionHandler) {
        super(parent, executor, false, DEFAULT_MAX_PENDING_TASKS, rejectedExecutionHandler);

        provider = selectorProvider;
        final SelectorTuple selectorTuple = openSelector();
        selector = selectorTuple.selector;
        unwrappedSelector = selectorTuple.unwrappedSelector;
        selectStrategy = strategy;
    }

private SelectorTuple openSelector() {
        final Selector unwrappedSelector;
        try {
            unwrappedSelector = provider.openSelector();
        } catch (IOException e) {
            throw new ChannelException("failed to open a new selector", e);
        }
                if (DISABLE_KEYSET_OPTIMIZATION) {
            return new SelectorTuple(unwrappedSelector);
        }
                ...
}

從上面這段代碼我們可以看出這幾點

  1. NioEventLoop裏面保存了SelectorProvider selectorProvider, Selector selector, unwrappedSelector(類型是KQueueSelectorImpl)
  2. selector, unwrappedSelector是通過provider.openSelector()打開的.
    根據2.3段的介紹,provider之前介紹的類型是KQueueSelectorProvider,然後它的openSelector會生成一個KQueueSelectorImpl
    所以provider.openSelector()得到是KQueueSelectorImpl,KQueueSelectorImpl的繼承關係如下:

    56. Netty源代碼分析-服務器初始化 NioEventLoopGroup實例化

繼續往回看,看MultithreadEventExecutorGroup的構造函數。

2.2.7 newChooser

EventExecutorChooserFactory.EventExecutorChooser chooser;

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

chooser = chooserFactory.newChooser(children);

上面代碼我們可以看到:

  1. chooserFactory的類型是DefaultEventExecutorChooserFactory,所以newChooser調用的是DefaultEventExecutorChooserFactory.newChooser方法。
    如下:
public EventExecutorChooser newChooser(EventExecutor[] executors) {
        if (isPowerOfTwo(executors.length)) {
            return new PowerOfTwoEventExecutorChooser(executors);
        } else {
            return new GenericEventExecutorChooser(executors);
        }
    }
  1. 傳入的參數是children,也就是NioEventLoop數組
  2. DefaultEventExecutorChooserFactory INSTANCE是一個static final類型對象,也就是一種餓漢式的單例模式,如下:
public static final DefaultEventExecutorChooserFactory INSTANCE = new DefaultEventExecutorChooserFactory();

繼續看newChooser的實現

2.2.8 newChooser

newChooser的代碼就不貼了,上面就有,從上面代碼可以看到:

  1. isPowerOfTwo是用來判斷一個整數是否是2的冪,比如(2,4, 8,16,32等等),它的實現方式如下:
private static boolean isPowerOfTwo(int val) {
        return (val & -val) == val;
    }

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)];
        }
    }       

這種實現方法感覺比較優雅和高效,首先拿到-val,也就是val的二進制倒轉,然後+1。再做&運算。
大家自己可以拿到數字舉個例子,比較巧妙。後續自己寫代碼可以借鑑,這是讀源碼的一個好處,可以學習到別人很多優秀的寫法。

  1. PowerOfTwoEventExecutorChooser和GenericEventExecutorChooser的不同之處在於next方法的算法不一樣,作用都是從NioEventLoop數組裏面選出一個NioEventLoop對象來。

但是說實話,我沒有想到這兩種算法有什麼區別,如果誰知道,請告訴我,謝謝。

return executors[idx.getAndIncrement() & executors.length - 1];

return executors[Math.abs(idx.getAndIncrement() % executors.length)];

繼續往回走,MultithreadEventExecutorGroup的構造函數就基本看完了。

三. 總結

我們來總結下NioEventLoopGroup的實例化過程,可以得到以下幾點。

1. NioEventLoopGroup的父類MultithreadEventExecutorGroup包含一個NioEventLoop數組children,數組的大小等於nThreads線程數目。如果沒有指定,默認一般是cpu核數 x 2

2. NioEventLoopGroup和NioEventLoop一樣都是繼承自Executor,但是NioEventLoopGroup又包含多個NioEventLoop(children數組),這種關係有點像android裏面ViewGroup和View的關係。或者裝飾者模式?

3. NioEventLoopGroup繼承自MultithreadEventLoopGroup,而NioEventLoop繼承自SingleThreadEventLoop,從名字看,不知道和多線程,單線程有沒有關係。

4. MultithreadEventLoopGroup有個chooser,執行next方法的時候,會選擇下一個NioEventLoop對象,雖然並不知道兩個chooser算法有何區別。

5. NioEventLoopGroup裏面重寫了newChild方法,裏面實例化NioEventLoop。

6. NioEventLoop裏面包含了Selector,類型是KQueueSelectorImpl

SelectorProvider provider
SelectStrategy selectStrategy

SelectStrategy這個我們上面我們沒有關注,其實它是NioEventLoopGroup構造函數傳進去的,如下:

public NioEventLoopGroup(
            int nThreads, Executor executor, final SelectorProvider selectorProvider) {
        this(nThreads, executor, selectorProvider, DefaultSelectStrategyFactory.INSTANCE);
    }

public final class DefaultSelectStrategyFactory implements SelectStrategyFactory {
    public static final SelectStrategyFactory INSTANCE = new DefaultSelectStrategyFactory();

    private DefaultSelectStrategyFactory() { }

    @Override
    public SelectStrategy newSelectStrategy() {
        return DefaultSelectStrategy.INSTANCE;
    }
}

final class DefaultSelectStrategy implements SelectStrategy {
    static final SelectStrategy INSTANCE = new DefaultSelectStrategy();

    private DefaultSelectStrategy() { }

    @Override
    public int calculateStrategy(IntSupplier selectSupplier, boolean hasTasks) throws Exception {
        return hasTasks ? selectSupplier.get() : SelectStrategy.SELECT;
    }
}

所以SelectStrategy的實現類是DefaultSelectStrategy.

在理清楚NioEventLoopGroup實例化的過程之後,我們下一篇繼續按照源代碼分析Netty服務器端的源代碼。

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