Reactor三种线程模型与Netty线程模型
一、Reactor三種線程模型
1.1、單線程模型
? ? ? ?單個線程以非阻塞IO或事件IO處理所有IO事件,包括連接、讀、寫、異常、關(guān)閉等等。單線程Reactor模型基于同步事件分離器來分發(fā)事件,這個同步事件分離器,可以看做是一個單線程的while循環(huán)。下圖描述了單線程模型的處理過程,看起來與網(wǎng)上大部分資料的圖片不同,但本質(zhì)是相同的。
? ? ? ?注意上面的Selector之所以會有OP_ACEEPT事件,是因為在單線程模型中,Selector輪詢的是監(jiān)聽套接字與已連接客戶端套接字的所有IO事件。
? ? ? ?單線程處理所有IO事件的弊端很明顯。沒能利用計算機CPU多核的特性,一個線程某個時刻只能處理單個IO事件,此時如果有其他描述符有IO事件就緒(如來了一個新的連接),這些IO事件將暫時得不到處理。
? ? ? ?C++框架libevent中,基于event_base_loop做消息輪詢,使用event_base_dispatch來分發(fā)IO消息,本質(zhì)上是對上述模型的封裝。如果不使用evthread_use_pthreads,則其默認就是單線程模型處理請求。
1.2、多線程模型
? ? ? ?一個線程/進程接收連接、一組線程/進程處理IO讀寫事件。也就是將accept的線程與處理讀、寫等IO事件的線程分離,并且使用m多個線程以非阻塞IO或者事件IO來處理n個套接字的IO事件,這里的n一般遠大于m,線程數(shù)m一般取CPU邏輯核心數(shù)的1-3倍,而套接字數(shù)n則取決于請求數(shù)和進程可以打開的最大描述符個數(shù)。下圖是多線程模型
? ? ? ?可以看到,這里把客戶端的已連接套接字,轉(zhuǎn)交給某個IO線程之后,由此線程輪詢處理其之后的所有IO事件,這實際參考了netty4的線程模型設(shè)計。實際reactor的多線程模型,并不需要將已連接套接字綁定在某個線程上,也可以統(tǒng)一放在連接池中,由多個IOWork線程從池中取連接進行輪詢并處理,但這樣會復雜很多,而且容易出問題,比如說不同線程從同一個channel收到了write事件,這就類似驚群問題了;并且多線程并發(fā)操作同一個channel,后續(xù)很可能需要你將IO事件進行同步,與其如此,不如直接將channel綁定到一個線程,讓channel上觸發(fā)與處理IO事件邏輯上同步。netty3中channel(已連接套接字)入站事件由固定線程處理,出站事件由觸發(fā)的線程處理,netty4中修改了設(shè)計,將channel綁定到固定的eventloop(線程)。
? ? ? ?另外一點,每個已連接套接字的IO事件由固定線程處理,不代表事件也一定由此線程觸發(fā),恰恰相反,實際業(yè)務中,讀(入站)事件來自于客戶端寫數(shù)據(jù)觸發(fā),而寫(出站)事件往往由別的線程觸發(fā),例如在發(fā)起一個異步mysql操作完成之后,在異步回調(diào)線程中寫結(jié)果數(shù)據(jù)來觸發(fā)套接字的出站。
1.3、從多線程模型
? ? ? ?一組線程/進程接收連接、一組線程/進程處理IO讀寫事件。它與多線程模型的主要區(qū)別在于其使用一組線程或進程在一個共享的監(jiān)聽套接字上accept連接。這么做的原因是為了應付單個線程/進程不足以快速處理內(nèi)核中監(jiān)聽套接字的已連接套接字隊列(并發(fā)量極大)的情況。如下:?
? ? 主從多線程模型,有可能引起驚群效應。不過這個問題已經(jīng)漸漸被規(guī)避,內(nèi)核可以保證連接只被唯一一個accept調(diào)用所獲取,其余對此連接的accept調(diào)用將失敗。
二、Netty支持的線程模型
? ? ? ?Netty支持單線程、多線程模型、主從多線程模型。但經(jīng)本人多次測試、調(diào)試發(fā)現(xiàn),ServerBootstrap默認不會使用主從多線程模型。雖然server支持設(shè)置EventLoopGroup(多個EventLoop)。但實際對于一個本地地址(IP+端口)進行accept,netty只會綁定到一個EventLoop上,故只會創(chuàng)建一個線程處理。
? ? ? ?按本人的理解,Boss EventLoopGroup(Master EventLoopGroup,參數(shù)nThreads不為1)的作用主要用在對共享的監(jiān)聽套接字或者多個本地地址監(jiān)聽,對多個本地地址進行監(jiān)聽一般表示一個JVM中有多個server,即有多個ServerBootStrap,這時,Boss EventLoopGroup可以通過共享給這多個ServerBootStrap起到作用(創(chuàng)建多個boss/master Thread)。
? ? ? ?以下面的代碼為例MASTER_THREAD_CNT為4,但netty實際只會使用第一個EventLoop,只會給第一個EventLoop創(chuàng)建線程。
?
調(diào)試跟蹤源碼,可以明白netty的邏輯。
在ServerBootstrap繼承的initAndRegister方法中,調(diào)用MultithreadEventLoopGroup#register方法,此方法調(diào)用this.next獲取當前索引的下一個(索引位0,即是第一個)EventLoop。
然后register方法進一步調(diào)用register方法,在register中執(zhí)行eventLoop.execute,這里才會真正為監(jiān)聽套接字創(chuàng)建第一個輪詢線程。
?
? ? ? ?問題就在于在ServerBootstrap上調(diào)用bind方法,初始化監(jiān)聽socket并綁定EventLoop時,是調(diào)用的next方法。因此netty只會初始化第一個MasterEventLoop,如果想將MasterEventLoopGroup中的每個EventLoop都初始化,很顯然,需要重復綁定多個監(jiān)聽套接字或者多次綁定一個可共享的套接字。
總結(jié)
以上是生活随笔為你收集整理的Reactor三种线程模型与Netty线程模型的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 论文浅尝 | 5 篇顶会论文带你了解知识
- 下一篇: 互联网架构:常用基础中间件介绍