十年網(wǎng)站開(kāi)發(fā)經(jīng)驗(yàn) + 多家企業(yè)客戶 + 靠譜的建站團(tuán)隊(duì)
量身定制 + 運(yùn)營(yíng)維護(hù)+專業(yè)推廣+無(wú)憂售后,網(wǎng)站問(wèn)題一站解決
線程模型Netty怎么用,相信很多沒(méi)有經(jīng)驗(yàn)的人對(duì)此束手無(wú)策,為此本文總結(jié)了問(wèn)題出現(xiàn)的原因和解決方法,通過(guò)這篇文章希望你能解決這個(gè)問(wèn)題。
創(chuàng)新互聯(lián)建站主營(yíng)環(huán)翠網(wǎng)站建設(shè)的網(wǎng)絡(luò)公司,主營(yíng)網(wǎng)站建設(shè)方案,重慶APP開(kāi)發(fā),環(huán)翠h5重慶小程序開(kāi)發(fā)公司搭建,環(huán)翠網(wǎng)站營(yíng)銷推廣歡迎環(huán)翠等地區(qū)企業(yè)咨詢
眾所周知,netty是高性能的原因源于其使用的是NIO,但是這只是其中一方面原因,其IO模型上決定的。另一方面源于其線程模型的設(shè)計(jì),良好的線程模型設(shè)計(jì),能夠減少線程上下文切換,減少甚至避免鎖的競(jìng)爭(zhēng)(無(wú)鎖化設(shè)計(jì))帶來(lái)的開(kāi)銷。
Reactor模式是一種軟件程序設(shè)計(jì)模式,它由Jim Coplien和Douglas C. Schmidt在1995年發(fā)布,主要用于處理一個(gè)或者多個(gè)客戶端發(fā)起請(qǐng)求。
從一個(gè)客戶端連接到日志服務(wù)器,然后發(fā)送請(qǐng)求的兩個(gè)流程來(lái)看Reactor模式。其中有三種組件:
Dispacther:分發(fā)客戶端的請(qǐng)求事件
Acceptor:接受客戶端連接事件
Handler:處理客戶端發(fā)起的請(qǐng)求事件

日志服務(wù)端注冊(cè)Acceptor至Dispatcher
日志服務(wù)端調(diào)用分發(fā)器內(nèi)部的handle_events方法
分發(fā)器開(kāi)始在多路監(jiān)聽(tīng)器(一般為OS的select、epoll)上等待客戶端請(qǐng)求
客戶端連接至日志服務(wù)器
分發(fā)器通知Acceptor連接事件
Accetor接受一個(gè)新的連接

客戶端使用上部分建立的連接發(fā)送記錄日志的請(qǐng)求
客戶端分發(fā)器將記錄日志的時(shí)間通知給Handler
Handler處理讀請(qǐng)求然后內(nèi)部在傳遞給下個(gè)Handler繼續(xù)處理
最終Handler進(jìn)行寫(xiě)響應(yīng)
返回到分發(fā)器,分發(fā)器繼續(xù)事件循環(huán)等待處理下個(gè)事件
以上的幾種組件的作用和處理連接和請(qǐng)求事件的模式就是Reactor模式。
以上的Reactor模式只是簡(jiǎn)單的設(shè)計(jì)模型,對(duì)于每種程序語(yǔ)言設(shè)計(jì)而言,仍然需要做一些改變?;贘ava的NIO如何使用該模式構(gòu)建高性能可伸縮的服務(wù),并發(fā)大神Doug Lea在他的網(wǎng)站上發(fā)布過(guò)一篇論文《Scalable IO in Java》。
這篇論文中主要談及的話題是如何構(gòu)建高性能可擴(kuò)展的IO,其中就是基于Reactor模式進(jìn)行了演進(jìn)。
其中涉及到以下組件:
Reactor: 響應(yīng)IO事件,分發(fā)至相應(yīng)的Handlers
Handlers: 執(zhí)行非阻塞的IO操作,

其中客戶端發(fā)送請(qǐng)求至服務(wù)端,Reactor響應(yīng)其IO事件。
如果是建立連接的請(qǐng)求,則將其分發(fā)至acceptor,由其接受連接,然后再將其注冊(cè)至分發(fā)器。
如果是讀請(qǐng)求,則分發(fā)至Handler,由其讀出請(qǐng)求內(nèi)容,然后對(duì)內(nèi)容解碼,然后處理計(jì)算,再對(duì)響應(yīng)編碼,最后發(fā)送響應(yīng)
在整個(gè)過(guò)程中都是使用單線程,無(wú)論是Reactor線程和后續(xù)的Handler處理都只使用一個(gè)線程。
但是單線程無(wú)疑會(huì)降低性能,所以需要增加線程提供擴(kuò)展。

為了能夠提高擴(kuò)展性,需要在單線程的模型上增加線程,主要從兩個(gè)方面利用多線程發(fā)揮多核的應(yīng)用優(yōu)勢(shì):
Worker Threads,Reactor應(yīng)該能夠快速的觸發(fā)事件,防止Handler處理的延遲Reactor的響應(yīng)導(dǎo)致事件積累,最終導(dǎo)致客戶端連接請(qǐng)求的積壓,甚至服務(wù)端的句柄數(shù)耗盡,服務(wù)停止響應(yīng)。所以在Handlers的處理中使用工作多線程
Multiple Reactor Threads,使用多個(gè)Reactor線程用于響應(yīng)客戶端發(fā)起的事件,可以使用多個(gè)Reactor分擔(dān)負(fù)載
以上多線程的Reactor處理模式中,Reactor線程仍然是單線程,負(fù)責(zé)acceptor和IO read/send。但是對(duì)于請(qǐng)求的解碼以及業(yè)務(wù)處理和響應(yīng)的編碼都是有work thread pool負(fù)責(zé)。
上述的多線程模式解決了Handler降低Reactor的響應(yīng),同時(shí)也提升了Handler的處理效率。但是Reactor仍然是單線程,對(duì)于大量的網(wǎng)絡(luò)事件,其仍然有負(fù)載壓力。為了能夠使用多線程分擔(dān)壓力,演進(jìn)出多Reactor:

其中主Reactor響應(yīng)用戶的連接事件,然后分發(fā)給acceptor,由其創(chuàng)建新的子Reactor。多個(gè)子Reactor分別處理各自的IO事件,比如read/write,然后再將其交給work thread pool進(jìn)行解碼,業(yè)務(wù)處理,編碼。
多Reactor的設(shè)計(jì)通過(guò)將TCP連接建立和IO read/write事件分離至不同的Reactor,從而分擔(dān)單個(gè)Reactor的壓力,提升其響應(yīng)能力。
在認(rèn)識(shí)了Reactor設(shè)計(jì)模式和基于Reactor構(gòu)建高性能可擴(kuò)展的IO后,再來(lái)看netty的線程模型就顯得簡(jiǎn)單的多了。
netty的線程模型設(shè)計(jì)正是Reactor模式的變種。以上的三種Reactor模式,在netty中都能非常好的得到了支持。在netty中主要通過(guò)參數(shù)配置來(lái)切換以上的各種模式。
netty中有EventLoopGroup和EventLoop兩個(gè)類,它們是實(shí)現(xiàn)Reactor的關(guān)鍵之所在。EventLoop正如其名,其中包包含一個(gè)Selector選擇器和一段循環(huán)邏輯。通過(guò)不斷循環(huán)獲取Selector上的就緒事件然后進(jìn)行處理。EventLoopGroup是包含一組EventLoop的組,通過(guò)其可以產(chǎn)生一個(gè)EventLoop。
在閱讀了netty官網(wǎng)給出的Demo后,可以知道,在創(chuàng)建一個(gè)Server時(shí)都會(huì)創(chuàng)建兩個(gè)EventLoopGroup,分別為boss和work。前者用戶Main Reactor,后者用于Sub Reactor和WorkThreadPool。
每次Main Reactor通過(guò)Selector得到客戶端建立連接的請(qǐng)求后,就從work EventLoopGroup中獲取一個(gè)EventLoop,然后將建立的連接對(duì)應(yīng)的Socket抽象SocketChannel綁定到EventLoop上,形成了新的Sub Reactor。
在了解了netty的線程模型后,下面首先看下各種模式下的netty的參數(shù)配置。
通過(guò)構(gòu)造一個(gè)EventLoop,將其用作Reactor和WorkThread,即是單線程模式。
EventLoopGroup eventLoopGroup = new NioEventLoopGroup(); EventLoop bossLoop = eventLoopGroup.next(); EventLoop workLoop = reactorLoop; ServerBootstrap b = new ServerBootstrap(); b.group(reactorLoop, workLoop);
boss和work使用同一個(gè)EventLoop,可以實(shí)現(xiàn)單線程Reactor。
Reactor使用單線程,然后Work使用多線程,即是多線程模型。
EventLoopGroup eventLoopGroup = new NioEventLoopGroup(); EventLoop bossLoop = eventLoopGroup.next(); EventLoopGroup workLoopGroup = new NioEventLoopGroup(); ServerBootstrap b = new ServerBootstrap(); b.group(reactorLoop, workLoopGroup);
以上的多線程Reactor模式,便是多Reactor模式。bossLoop是主Reactor,其通過(guò)事件循環(huán)創(chuàng)建TCP連接,然后將連接的SocketChannel抽象綁定到workLoopGroup中的EventLoop上,形成Sub Reactor。
只是Main Reactor是單線程進(jìn)行事件循環(huán)。雖然也可以構(gòu)造多線程,但是沒(méi)有什么實(shí)際意義。因?yàn)閚etty中在綁定端口時(shí)只會(huì)使用Group中的一個(gè)EventLoop綁定到Selector上,即是使用了EventLoopGroup。
當(dāng)然對(duì)于同個(gè)應(yīng)用如果監(jiān)聽(tīng)多個(gè)端口,使用多個(gè)ServerBootStrap共享一個(gè)boss,那樣Main Reactor也是多線程模式,才有意義。
看完上述內(nèi)容,你們掌握線程模型Netty怎么用的方法了嗎?如果還想學(xué)到更多技能或想了解更多相關(guān)內(nèi)容,歡迎關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道,感謝各位的閱讀!