图灵学院Java架构师五期笔记
緣起
日前在看netty的工作原理,對(duì)netty的線程模型很是不能理解,查閱了諸多資料,終于有了一些眉目。特此記錄,以備查閱。
閱讀對(duì)象
netty中的NIO編程模型是基于java Nio的封裝,所以需要讀者對(duì)java NIO有一定的了解,篇幅所限,本文不會(huì)對(duì)NIO再做詳述,有需要的讀者可以查看??JAVA BIO,NIO,AIO詳解(附代碼實(shí)現(xiàn))以及Netty的簡(jiǎn)介??
Netty的NIO線程模型
說(shuō)netty的線程模型之前,先說(shuō)傳統(tǒng)NIO的使用方式中的關(guān)鍵代碼
可以看到是當(dāng)前線程獲得了客戶端的連接之后再重新把channel注冊(cè)到selector上,同時(shí)把事件注冊(cè)為讀,這樣就可以開始讀消息了。也就是說(shuō)獲取tcp連接和讀取消息都是在同一個(gè)線程里面處理的。那么這種方式有什么問題呢?對(duì)于一些小流量應(yīng)用場(chǎng)景,可以使用單線程模型。但是對(duì)于高負(fù)載、大并發(fā)的應(yīng)用場(chǎng)景卻不合適,主要原因如下:
一個(gè) NIO 線程同時(shí)處理成百上千的鏈路,性能上無(wú)法支撐,即便 NIO 線程的 CPU 負(fù)荷達(dá)到 100%,也無(wú)法滿足海量消息的編碼、解碼、讀取和發(fā)送;
當(dāng) NIO 線程負(fù)載過(guò)重之后,處理速度將變慢,這會(huì)導(dǎo)致大量客戶端連接超時(shí),超時(shí)之后往往會(huì)進(jìn)行重發(fā),這更加重了 NIO 線程的負(fù)載,最終會(huì)導(dǎo)致大量消息積壓和處理超時(shí),成為系統(tǒng)的性能瓶頸;
可靠性問題:一旦 NIO 線程意外跑飛,或者進(jìn)入死循環(huán),會(huì)導(dǎo)致整個(gè)系統(tǒng)通信模塊不可用,不能接收和處理外部消息,造成節(jié)點(diǎn)故障。
那么你可能會(huì)想,既然一個(gè)線程不行,那我在讀取消息的時(shí)候采用線程池不就可以了嗎?的確是可行的,事實(shí)上,在Netty中也確實(shí)是這么做的。
netty的一個(gè)啟動(dòng)程序如下
可以看到開頭new了兩個(gè)EventLoopGroup,EventLoopGroup可以暫時(shí)理解為一個(gè)線程組。其中bossGroup負(fù)責(zé)處理客戶端的 TCP 連接請(qǐng)求,如果系統(tǒng)只有一個(gè)服務(wù)端端口需要監(jiān)聽,則建議 bossGroup 線程組線程數(shù)設(shè)置為 1,workerGroup 是真正負(fù)責(zé) I/O 讀寫操作的線程組
先看一張netty的執(zhí)行流程圖
圖中可以看到boosGroup在接收到請(qǐng)求后會(huì)重新注冊(cè)到workerGroup上,也就是對(duì)應(yīng)了前面說(shuō)的bossGroup負(fù)責(zé)處理客戶端的 TCP 連接請(qǐng)求,而workerGroup是真正負(fù)責(zé) I/O 讀寫操作的線程組。就像軟件開發(fā)里面的boss負(fù)責(zé)分配任務(wù),而worker即程序員負(fù)責(zé)寫代碼。那么bossGroup是如何處理tcp的連接請(qǐng)求同時(shí)把他注冊(cè)到workerGroup上的呢?
bossGroup是如何分配任務(wù)的
workerGroup中的每一個(gè)線程,都有一個(gè)多路復(fù)用器 Selector,bossGroup每接收到一個(gè)客戶端連接,就會(huì)從workerGroup選擇一個(gè)線程然后把channel注冊(cè)到它的Selector上。
偽代碼實(shí)現(xiàn)
boss中的代碼
可以看到,bossGroup接收到了新客戶端的請(qǐng)求后,就會(huì)調(diào)用一個(gè)算法,從多個(gè)worker中選取一個(gè)worker,然后調(diào)用worker的注冊(cè)方法,把這個(gè)新客戶端的channel注冊(cè)上去。我們?cè)倏磜orkerGroup中的注冊(cè)方法實(shí)現(xiàn)
可以看到,就是把新的channel注冊(cè)到自己的Selector上,注冊(cè)好后就會(huì)觸發(fā)workerGroup的堵塞代碼塊,這樣這個(gè)workerGroup中的這個(gè)線程就會(huì)開始讀取數(shù)據(jù),下面看看worker線程讀取數(shù)據(jù)的偽代碼實(shí)現(xiàn)(其實(shí)就是普通的NIO中讀取數(shù)據(jù)的方式)
總結(jié):bossGroup負(fù)責(zé)處理客戶端的 TCP 連接請(qǐng)求,bossGroup每接收到一個(gè)客戶端連接,就會(huì)從workerGroup選擇一個(gè)線程然后把channel注冊(cè)到它的Selector上,這樣的話請(qǐng)求接收和請(qǐng)求處理就通過(guò)不同的線程分開了,這也是netty高效的原因之一。當(dāng)然Netty高效的原因絕不僅僅是由于優(yōu)秀的線程模型的設(shè)計(jì),與Netty的編碼協(xié)議,virtual buffer,以及Zero-Copy(零拷貝)也息息相關(guān) 。
總結(jié)
以上是生活随笔為你收集整理的图灵学院Java架构师五期笔记的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Windows的进程创建和映像装入
- 下一篇: 智能合约 web3j Java_Java