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