摘要:接下來的兩篇文章,我將從源碼角度為大家深入淺出的剖析的線程模型工作機(jī)制。我們看一下的源碼通過的代碼發(fā)現(xiàn),實現(xiàn)了接口,其內(nèi)部會通過指定的默認(rèn)線程工廠來創(chuàng)建線程,并執(zhí)行相應(yīng)的任務(wù)。至此,初始化完成了。下一篇我們將詳細(xì)介紹,敬請期待。
我們都知道Netty的線程模型是基于React的線程模型,并且我們都知道Netty是一個高性能的NIO框架,那么其線程模型必定是它的重要貢獻(xiàn)之一。
在使用netty的服務(wù)端引導(dǎo)類ServerBootstrap或客戶端引導(dǎo)類Bootstrap進(jìn)行開發(fā)時,都需要通過group屬性指定EventLoopGroup, 因為是開發(fā)NIO程序,所以我們選擇NioEventLoopGroup。
接下來的兩篇文章,我將從源碼角度為大家深入淺出的剖析Netty的React線程模型工作機(jī)制。
本篇側(cè)重點是NioEventLoopGroup。
首先我們先回顧一下,服務(wù)端初始化程序代碼(省略非相關(guān)代碼):
EventLoopGroup bossGroup = new NioEventLoopGroup(); EventLoopGroup workerGroup = new NioEventLoopGroup(); try { ServerBootstrap b = new ServerBootstrap(); b.group(bossGroup, workerGroup); ... // 已省略非相關(guān)代碼 // 偵聽8000端口 ChannelFuture f = b.bind(8000).sync(); f.channel().closeFuture().sync(); } finally { workerGroup.shutdownGracefully(); bossGroup.shutdownGracefully(); }
在分析源碼之前,我們先看看NioEventLoopGroup的類繼承結(jié)構(gòu)圖:
初始化bossGroup及workerGroup時,使用了NioEventLoopGroup的無參構(gòu)造方法,本篇將從此無參構(gòu)造入手,詳細(xì)分析NioEventLoopGroup的初始化過程。
首先我們看看NioEventLoopGroup的無參構(gòu)造方法:
public NioEventLoopGroup() { this(0); }
其內(nèi)部繼續(xù)調(diào)用器構(gòu)造方法,并指定線程數(shù)為0:
public NioEventLoopGroup(int nThreads) { this(nThreads, (Executor) null); }
繼續(xù)調(diào)用另一個構(gòu)造方法,指定線程為0,且Executor為null:
public NioEventLoopGroup(int nThreads, Executor executor) { this(nThreads, executor, SelectorProvider.provider()); }
在此構(gòu)造中,它會指定selector的輔助類 "SelectorProvider.provider()",我們繼續(xù)查看它的調(diào)用:
public NioEventLoopGroup( int nThreads, Executor executor, final SelectorProvider selectorProvider) { this(nThreads, executor, selectorProvider, DefaultSelectStrategyFactory.INSTANCE); }
此構(gòu)造方法中,初始化了一個默認(rèn)的選擇策略工廠,用于生成select策略:
public NioEventLoopGroup(int nThreads, Executor executor, final SelectorProvider selectorProvider, final SelectStrategyFactory selectStrategyFactory) { super(nThreads, executor, selectorProvider, selectStrategyFactory, RejectedExecutionHandlers.reject()); }
經(jīng)過上面一系列的構(gòu)造方法調(diào)用,此時個參數(shù)值對應(yīng)如下:
nThreads:0
executor: null
selectorProvider: SelectorProvider.provider()
selectStrategyFactory: DefaultSelectStrategyFactory.INSTANCE
以及指定了拒絕策略RejectedExecutionHandlers.reject()
接著其會調(diào)用父類MultithreadEventLoopGroup的MultithreadEventLoopGroup(int nThreads, Executor executor, Object... args)構(gòu)造方法
protected MultithreadEventLoopGroup(int nThreads, Executor executor, Object... args) { super(nThreads == 0 ? DEFAULT_EVENT_LOOP_THREADS : nThreads, executor, args); }
此構(gòu)造方法主要做了一件事,就是當(dāng)指定的線程數(shù)為0時,使用默認(rèn)的線程數(shù)DEFAULT_EVENT_LOOP_THREADS,此只是在MultithreadEventLoopGroup類被加載時完成初始化
private static final int DEFAULT_EVENT_LOOP_THREADS; static { DEFAULT_EVENT_LOOP_THREADS = Math.max(1, SystemPropertyUtil.getInt( "io.netty.eventLoopThreads", NettyRuntime.availableProcessors() * 2)); if (logger.isDebugEnabled()) { logger.debug("-Dio.netty.eventLoopThreads: {}", DEFAULT_EVENT_LOOP_THREADS); } }
所以根據(jù)代碼,我們得出,如果初始化NioEventLoopGroup未指定線程數(shù)時,默認(rèn)是CPU核心數(shù)*2
接著繼續(xù)調(diào)用父類MultithreadEventExecutorGroup的MultithreadEventExecutorGroup(int nThreads, Executor executor, Object... args)構(gòu)造方法
protected MultithreadEventExecutorGroup(int nThreads, Executor executor, Object... args) { this(nThreads, executor, DefaultEventExecutorChooserFactory.INSTANCE, args); }
在此構(gòu)造方法中,我們指定了一個EventExecutor的選擇工廠DefaultEventExecutorChooserFactory,此工廠主要是用于選擇下一個可用的EventExecutor, 其內(nèi)部有兩種選擇器, 一個是基于位運算,一個是基于普通的輪詢,它們的代碼分別如下:
基于位運算的選擇器PowerOfTwoEventExecutorChooser
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]; } }
基于普通輪詢的選擇器GenericEventExecutorChooser
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)]; } }
我們接著回到剛剛的構(gòu)造器,其內(nèi)部會繼續(xù)調(diào)用MultithreadEventExecutorGroup的另一個構(gòu)造方法,此構(gòu)造方法是NioEventLoopGroup的核心代碼
protected MultithreadEventExecutorGroup(int nThreads, Executor executor, EventExecutorChooserFactory chooserFactory, Object... args) { // 為了便于代碼剖析,以省略非相關(guān)代碼 // 初始化executor if (executor == null) { executor = new ThreadPerTaskExecutor(newDefaultThreadFactory()); } // 初始化EventExecutor children = new EventExecutor[nThreads]; for (int i = 0; i < nThreads; i ++) { children[i] = newChild(executor, args); } // 生成選擇器對象 chooser = chooserFactory.newChooser(children); }
此構(gòu)造方法主要做了三件事:
1、初始化executor為ThreadPerTaskExecutor的實例:
通過前面的構(gòu)造方法調(diào)用,我們知道executor為null,所以在此構(gòu)造方法中,executor會被初始化為ThreadPerTaskExecutor實例。我們看一下ThreadPerTaskExecutor的源碼:
public final class ThreadPerTaskExecutor implements Executor { private final ThreadFactory threadFactory; public ThreadPerTaskExecutor(ThreadFactory threadFactory) { if (threadFactory == null) { throw new NullPointerException("threadFactory"); } this.threadFactory = threadFactory; } @Override public void execute(Runnable command) { threadFactory.newThread(command).start(); } }
通過ThreadPerTaskExecutor 的代碼發(fā)現(xiàn),ThreadPerTaskExecutor 實現(xiàn)了Executor接口,其內(nèi)部會通過newDefaultThreadFactory()指定的默認(rèn)線程工廠來創(chuàng)建線程,并執(zhí)行相應(yīng)的任務(wù)。
2、初始化EventExecutor數(shù)組children
在MultithreadEventExecutorGroup的構(gòu)造方法中我們看到,EventExecutor數(shù)組children初始化時是通過newChild(executor, args)實現(xiàn)的,而newChild的在MultithreadEventExecutorGroup中是個抽象方法
protected abstract EventExecutor newChild(Executor executor, Object... args) throws Exception;
根據(jù)最開始的類繼承結(jié)構(gòu)圖,我們在NioEventLoopGroup中找到了newChild的實現(xiàn)
@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]); }
所以從此newChild的實現(xiàn)中,我們可以看出MultithreadEventExecutorGroup的children,其實就是對應(yīng)的一組NioEventLoop對象。 關(guān)于NioEventLoop下一篇會作詳細(xì)介紹。
3、根據(jù)我們指定的選擇器工廠,綁定NioEventLoop數(shù)組對象
chooser = chooserFactory.newChooser(children);
在前面的構(gòu)造方法中,我們指定了chooserFactory為DefaultEventExecutorChooserFactory,在此工廠內(nèi)部,會根據(jù)children數(shù)組的長度來動態(tài)選擇選擇器對象,用于選擇下一個可執(zhí)行的EventExecutor,也就是NioEventLoop。
@Override public EventExecutorChooser newChooser(EventExecutor[] executors) { if (isPowerOfTwo(executors.length)) { return new PowerOfTwoEventExecutorChooser(executors); } else { return new GenericEventExecutorChooser(executors); } }
至此,NioEventLoopGroup初始化完成了。
通過上面的代碼分析,在NioEventLoopGroup初始化的過程中,其實就是初始化了一堆可執(zhí)行的Executor數(shù)組,然后根據(jù)某種chooser策略,來選擇下一個可用的executor。
我們再回顧總結(jié)一下:
1、NioEventLoopGroup初始化時未指定線程數(shù),那么會使用默認(rèn)線程數(shù),
即 線程數(shù) = CPU核心數(shù) * 2;
2、每個NioEventLoopGroup對象內(nèi)部都有一組可執(zhí)行的NioEventLoop(NioEventLoop對象內(nèi)部包含的excutor對象為ThreadPerTaskExecutor類型)
3、每個NioEventLoopGroup對象都有一個NioEventLoop選擇器與之對應(yīng),其會根據(jù)NioEventLoop的個數(shù),動態(tài)選擇chooser(如果是2的冪次方,則按位運算,否則使用普通的輪詢)
所以通過上面的分析,我們得出NioEventLoopGroup主要功能就是為了選擇NioEventLoop,而真正的重點就在NioEventLoop中,它是整個netty線程執(zhí)行的關(guān)鍵。
下一篇我們將詳細(xì)介紹NioEventLoop,敬請期待。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/68100.html
摘要:對于,目前大家只知道是個線程組,其內(nèi)部到底如何實現(xiàn)的,它的作用到底是什么,大家也都不太清楚,由于篇幅原因,這里不作詳細(xì)介紹,后面會有文章作專門詳解。 在上一篇《ServerBootstrap 與 Bootstrap 初探》中,我們已經(jīng)初步的了解了ServerBootstrap是netty進(jìn)行服務(wù)端開發(fā)的引導(dǎo)類。 且在上一篇的服務(wù)端示例中,我們也看到了,在使用netty進(jìn)行網(wǎng)絡(luò)編程時,我...
摘要:而用于主線程池的屬性都定義在中本篇只是簡單介紹了一下引導(dǎo)類的配置屬性,下一篇我將詳細(xì)介紹服務(wù)端引導(dǎo)類的過程分析。 從Java1.4開始, Java引入了non-blocking IO,簡稱NIO。NIO與傳統(tǒng)socket最大的不同就是引入了Channel和多路復(fù)用selector的概念。傳統(tǒng)的socket是基于stream的,它是單向的,有InputStream表示read和Outpu...
摘要:一些想法這個系列想開很久了,自己使用也有一段時間了,利用也編寫了一個簡單的框架,并運用到工作中了,感覺還不錯,趁著這段時間工作不是很忙,來分析一波源碼,提升下技術(shù)硬實力。 一些想法 這個系列想開很久了,自己使用netty也有一段時間了,利用netty也編寫了一個簡單的框架,并運用到工作中了,感覺還不錯,趁著這段時間工作不是很忙,來分析一波源碼,提升下技術(shù)硬實力。 結(jié)構(gòu) 這里先看下net...
摘要:本篇將通過實例化過程,來深入剖析。及初始化完成后,它們會相互連接。我們在回到的構(gòu)造方法父類構(gòu)造方法調(diào)用完成后,還要初始化一下自己的配置對象是的內(nèi)部類而又是繼承自,通過代碼分析,此對象就是就會對底層一些配置設(shè)置行為的封裝。 根據(jù)上一篇《Netty4.x 源碼實戰(zhàn)系列(二):服務(wù)端bind流程詳解》所述,在進(jìn)行服務(wù)端開發(fā)時,必須通過ServerBootstrap引導(dǎo)類的channel方法來...
摘要:在上一篇源碼實戰(zhàn)系列三全剖析中,我們詳細(xì)分析了的初始化過程,并得出了如下結(jié)論在中,每一個都有一個對象,并且其內(nèi)部本質(zhì)上就是一個雙向鏈表本篇我們將深入源碼內(nèi)部,對其一探究竟,給大家一個全方位解析。 在上一篇《Netty4.x 源碼實戰(zhàn)系列(三):NioServerSocketChannel全剖析》中,我們詳細(xì)分析了NioServerSocketChannel的初始化過程,并得出了如下結(jié)論...
閱讀 1992·2021-11-22 14:45
閱讀 2612·2021-10-12 10:11
閱讀 776·2021-09-22 10:02
閱讀 1234·2019-08-30 15:55
閱讀 1147·2019-08-30 15:54
閱讀 3258·2019-08-30 15:54
閱讀 1197·2019-08-29 17:16
閱讀 3093·2019-08-28 17:55