56. Netty源代码分析-服务器初始化 NioEventLoopGroup实例化
一. 代碼下載
Netty代碼下載和編譯參考前一篇Netty文章
https://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>() {@Overrideprotected 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。
如下:
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());}我們可以看到幾點
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);} }這段代碼我們也可以看到幾點:
這個先記下來,也許后面分析會有用,繼續分析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));}上面這段代碼我們可以看到這幾點:
DEFAULT_EVENT_LOOP_THREADS如果沒有配置io.netty.eventLoopThreads的話,一般是cpu核數*2
繼續父類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. 循環初始化childrenboolean 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. 實例化chooserfinal FutureListener<Object> terminationListener = new FutureListener<Object>() {@Overridepublic 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;實例類是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.(看名字是單線程和多線程的區別?)
繼續看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);}... }從上面這段代碼我們可以看出這幾點
selector, unwrappedSelector是通過provider.openSelector()打開的.
根據2.3段的介紹,provider之前介紹的類型是KQueueSelectorProvider,然后它的openSelector會生成一個KQueueSelectorImpl
所以provider.openSelector()得到是KQueueSelectorImpl,KQueueSelectorImpl的繼承關系如下:
繼續往回看,看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);上面代碼我們可以看到:
如下:
繼續看newChooser的實現
2.2.8 newChooser
newChooser的代碼就不貼了,上面就有,從上面代碼可以看到:
這種實現方法感覺比較優雅和高效,首先拿到-val,也就是val的二進制倒轉,然后+1。再做&運算。
大家自己可以拿到數字舉個例子,比較巧妙。后續自己寫代碼可以借鑒,這是讀源碼的一個好處,可以學習到別人很多優秀的寫法。
但是說實話,我沒有想到這兩種算法有什么區別,如果誰知道,請告訴我,謝謝。
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() { }@Overridepublic SelectStrategy newSelectStrategy() {return DefaultSelectStrategy.INSTANCE;} }final class DefaultSelectStrategy implements SelectStrategy {static final SelectStrategy INSTANCE = new DefaultSelectStrategy();private DefaultSelectStrategy() { }@Overridepublic int calculateStrategy(IntSupplier selectSupplier, boolean hasTasks) throws Exception {return hasTasks ? selectSupplier.get() : SelectStrategy.SELECT;} }所以SelectStrategy的實現類是DefaultSelectStrategy.
在理清楚NioEventLoopGroup實例化的過程之后,我們下一篇繼續按照源代碼分析Netty服務器端的源代碼。
轉載于:https://blog.51cto.com/483181/2118817
總結
以上是生活随笔為你收集整理的56. Netty源代码分析-服务器初始化 NioEventLoopGroup实例化的全部內容,希望文章能夠幫你解決所遇到的問題。