<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          【死磕 NIO】— Reactor 模式就一定意味著高性能嗎?

          共 3672字,需瀏覽 8分鐘

           ·

          2021-11-06 11:47

          大家好,我是大明哥,我又來了。

          為什么是 Reactor

          一般所有的網(wǎng)絡(luò)服務(wù),一般分為如下幾個(gè)步驟:

          • 讀請(qǐng)求(read request)

          • 讀解析(read decode)

          • 處理程序(process service)

          • 應(yīng)答編碼 (encode reply)

          • 發(fā)送應(yīng)答(send reply)

          接下來,大明哥就來分析解決這個(gè)問題的最佳實(shí)踐。

          單線程模式

          對(duì)于很多小伙伴來說,最簡(jiǎn)單,最傳統(tǒng)的方式就是一個(gè)方法來處理所有的請(qǐng)求,這種實(shí)現(xiàn)方式最簡(jiǎn)單,也是最保險(xiǎn)的方式。

          這種方式實(shí)現(xiàn)起來雖然簡(jiǎn)單,但是性能不行,如果其中有一個(gè)請(qǐng)求因?yàn)槟撤N原因阻塞了,則他后面的所有請(qǐng)求都會(huì)阻塞在那里,同時(shí)他也沒法利用多 CPU 的性能,性能嚴(yán)重不足。

          多線程模式

          單線程的性能肯定不行,那就調(diào)整為多線程方式。

          每來一個(gè)請(qǐng)求就會(huì)創(chuàng)建一個(gè)線程來處理,這種方式雖然不會(huì)像 單線程模式 一樣,一個(gè)線程會(huì)阻塞所有的請(qǐng)求,但是他依然很大的問題:

          • 當(dāng)客戶端多,并發(fā)大的時(shí)候,需要?jiǎng)?chuàng)建大量線程來處理,線程的創(chuàng)建和銷毀也很消耗資源,會(huì)導(dǎo)致整個(gè)系統(tǒng)的的資源占用較大

          • 同樣無法應(yīng)對(duì)高性能和高并發(fā)

          線程池模式

          既然多線程模式需要?jiǎng)?chuàng)建這么多線程,那么我們控制創(chuàng)建線程的個(gè)數(shù),采用資源復(fù)用 線程池 的方式,也就是我們不需要再為每一個(gè)連接創(chuàng)建一個(gè)線程,而是創(chuàng)建一個(gè)線程池,將連接分配給線程,然后一個(gè)線程可以處理多個(gè)鏈接。

          這種線程池的方式雖然解決了系統(tǒng)資源占用的問題,但是他依然帶了了一個(gè)新的問題,每一個(gè)線程如何高效地處理請(qǐng)求呢?在上篇文章中 【死磕NIO】— 阻塞IO,非阻塞IO,IO復(fù)用,信號(hào)驅(qū)動(dòng)IO,異步IO,這你真的分的清楚嗎?我們提到過在單個(gè)線程中如果當(dāng)前連接在進(jìn)行read操作時(shí),如果沒有數(shù)據(jù)可讀,則會(huì)發(fā)生阻塞,那么線程就沒有辦法繼續(xù)處理其他連接的業(yè)務(wù)了。那么怎么解決?將 read 操作改為非阻塞的方式,既然改為了非阻塞方式,那線程如何知道read 操作有數(shù)據(jù)可讀了呢?

          • 第一種方式,則是不斷的去輪詢,但是輪詢要消耗 CPU的,而且隨著輪詢的線程多了,輪詢的效率會(huì)越來越低

          • 第二種方式,事件驅(qū)動(dòng)。當(dāng)線程關(guān)心的事件發(fā)生了,比如read 有數(shù)據(jù)可讀了,則通知相對(duì)應(yīng)的線程進(jìn)行處理

          Reactor 模式

          第二種方式就是 I/O多路復(fù)用。I/O多路復(fù)用就是通過一種機(jī)制,一個(gè)線程可以監(jiān)視多個(gè)描述符,一旦某個(gè)描述符就緒(一般是讀就緒或者寫就緒),能夠通知線程進(jìn)行相應(yīng)的讀寫操作。目前支持 IO多路復(fù)用技術(shù)有:

          • Linux:selectpollepoll

          • MAC:kqueue

          • Windows:select

          監(jiān)聽線程幫助我們監(jiān)聽哪些線程的事件已發(fā)生,發(fā)生后則通知相對(duì)應(yīng)的線程進(jìn)行處理,這樣就可以避免進(jìn)行很多無用的操作。對(duì)處理線程而言,整個(gè)處理過程只有調(diào)用 selectpollepoll 的時(shí)候才會(huì)阻塞,其他時(shí)段,他可以處理其他的事情,這樣整個(gè)線程會(huì)被充分利用起來,這樣就高效很多了。

          什么是 Reactor模式

          上面講了 Reactor 模式的演變,那什么是 Reactor 模式呢?

          wiki上是這樣定義的:

          ?

          Reactor 模式也叫做反應(yīng)器設(shè)計(jì)模式,它是一種為處理服務(wù)請(qǐng)求并發(fā)提交到一個(gè)或者多個(gè)服務(wù)處理程序的事件設(shè)計(jì)模式。當(dāng)請(qǐng)求抵達(dá)后,服務(wù)處理程序使用解多路分配策略,然后同步地派發(fā)這些請(qǐng)求至相關(guān)的請(qǐng)求處理程序。

          簡(jiǎn)要概括就是:**將消息放到了一個(gè)隊(duì)列中,通過異步線程池對(duì)其進(jìn)行消費(fèi)。**暫時(shí)理解成下面這個(gè)樣子:

          對(duì)于Reactor模式來說,他并沒隊(duì)列,每當(dāng)有一個(gè) Event 輸入到 Server端時(shí),Service Handler 會(huì)將其轉(zhuǎn)發(fā)(dispatch)相對(duì)應(yīng)的handler進(jìn)行處理。

          Reactor的組件主要包括三個(gè):

          • Reactor:派發(fā)器,將 client端的事件分發(fā)給相對(duì)應(yīng)的Handler

          • Acceptor:請(qǐng)求連接器,Reactor 接收到 client 連接事件后,會(huì)將其轉(zhuǎn)發(fā)給 Acceptor,Acceptor 則會(huì)接受 Client 的連接,建立對(duì)應(yīng)的Handler,并向 Reactor注冊(cè)此Handler

          • Handler:請(qǐng)求處理器,負(fù)責(zé)事件的處理。

          模型大致如下圖:

          Reactor 模式

          Reactor 模型中的Reactor可以是多個(gè)也可以是單個(gè),Handler同樣可以是單線程也可以是多線程,所以組合的模式大致有如下四種:

          • 單Reactor單線程/進(jìn)程

          • 單Reactor多線程/進(jìn)程

          • 多Reactor單線程/進(jìn)程

          • 多Reactor多線程/進(jìn)程

          其中第三種多Reactor單線程并沒有什么實(shí)際的意思,所以大明哥重點(diǎn)介紹第一、二、四種。

          單Reactor單線程/進(jìn)程

          • Reactor 線程通過 select (IO多路復(fù)用接口)監(jiān)聽事件,收到事件后通過Dispatch 來分發(fā)事件,事件會(huì)分發(fā)給Acceptor和Handler 兩個(gè)組件,具體是哪個(gè)組件要看事件的類型。

          • 如果事件類型為建立連接,則將事件分發(fā)給Acceptor,Acceptor會(huì)通過 accept 方法 獲取連接,并創(chuàng)建一個(gè) Handler 對(duì)象來處理后續(xù)的響應(yīng)事件。

          • 如果時(shí)間類型不是建立連接,則將該事件交由當(dāng)前連接的Handler來處理。

          優(yōu)缺點(diǎn)

          • 優(yōu)點(diǎn):該模型是將所有處理邏輯放在一個(gè)線程中實(shí)現(xiàn),模型簡(jiǎn)單,沒有多線程、進(jìn)程通信、競(jìng)爭(zhēng)的問題

          • 缺點(diǎn)

            • 由于只有一個(gè)線程,無法充分利用CPU,性能堪憂。同時(shí)Handler 在處理某個(gè)連接上的業(yè)務(wù)時(shí),整個(gè)進(jìn)程無法處理其他連接事件,很容易導(dǎo)致性能瓶頸。

            • 還有一個(gè)比較嚴(yán)重的可靠性問題,如果線程意外終止,或者進(jìn)入死循環(huán),則會(huì)導(dǎo)致整個(gè)線程都無法接受和處理事件了,造成節(jié)點(diǎn)故障。

          單Reactor多線程/進(jìn)程

          單線程存在性能瓶頸,那我們就引入多線程方案。

          Reactor 接受請(qǐng)求后,根據(jù)請(qǐng)求類型來進(jìn)行分發(fā),分發(fā)邏輯與 單Reactor單線程 模型一樣,不同之處在于Handler不在進(jìn)行業(yè)務(wù)處理了,它只負(fù)責(zé)接受和發(fā)送,Handler接受數(shù)據(jù)后,會(huì)將數(shù)據(jù)發(fā)送給 Worker 線程池中的線程處理,該線程才是處理業(yè)務(wù)的真正線程,線程將業(yè)務(wù)處理完成后,將數(shù)據(jù)發(fā)送給Handler,然后Handler 再send出去。

          優(yōu)缺點(diǎn)

          • 優(yōu)點(diǎn):由于Handler使用了多線程模式,則可以利用充分利用CPU的性能

          • 缺點(diǎn):

            • Handler使用多線程模式,則會(huì)涉及到數(shù)據(jù)共享的問題,需要考慮互斥,實(shí)現(xiàn)肯定比 單Reactor單線程模式復(fù)雜一些

            • 單Reactor,一個(gè)線程處理事件監(jiān)聽、分發(fā)、響應(yīng),對(duì)于高并發(fā)場(chǎng)景,容易造成性能瓶頸

          多Reactor多線程/進(jìn)程

          單Reactor多線程模式解決了Handler單線程的性能問題,但是Reactor還是單線程的,對(duì)于高并發(fā)場(chǎng)景還是會(huì)有性能瓶頸,所以需要對(duì)Reactor調(diào)整為 多線程模式

          • 主線程中的MainReactor對(duì)象通過select監(jiān)聽事件,接收到事件后通過Dispatch進(jìn)行分發(fā),如果事件類型為建立連接則將事件分發(fā)給Acceptor 進(jìn)行連接建立

          • 如果收到的事件不是連接,則他將事件分發(fā)個(gè)某個(gè)SubReactor,SubrReactor 將連接加入到連接隊(duì)列進(jìn)行監(jiān)聽,并創(chuàng)建Handler進(jìn)行各種事件處理

          • 如果有新的事件發(fā)生,SubReactor 則會(huì)調(diào)用當(dāng)前連接的Handler來進(jìn)行處理。Handler 通過read 讀取數(shù)據(jù)后,將數(shù)據(jù)發(fā)送給Worker線程進(jìn)行處理,Worker線程池則會(huì)分配線程進(jìn)行業(yè)務(wù)處理,處理完成后返回結(jié)果,Handler接受結(jié)果后,通過send發(fā)送給客戶端

          優(yōu)缺點(diǎn)

          • 優(yōu)點(diǎn):該模式主線程和子線程分工明確,主線程只負(fù)責(zé)接收新連接,子線程負(fù)責(zé)完成后續(xù)的業(yè)務(wù)處理,同時(shí)主線程和子線程的交互也很簡(jiǎn)單,子線程接收主線程的連接后,只管業(yè)務(wù)處理即可,無須關(guān)注主線程

          • 缺點(diǎn):模型復(fù)雜

          這種模式適用于高并發(fā)場(chǎng)景,廣泛運(yùn)用于各種項(xiàng)目中,如大名鼎鼎的Netty。

          Reactor 優(yōu)缺點(diǎn)

          Reactor模式有如下優(yōu)點(diǎn):

          • 響應(yīng)快,不必為單個(gè)同步時(shí)間所阻塞

          • 可以最大程度的避免復(fù)雜的多線程及同步問題,并且避免了多線程/進(jìn)程的切換開銷

          • 擴(kuò)展性好,可以方便的通過增加 Reactor 實(shí)例個(gè)數(shù)來充分利用 CPU 資源

          • 復(fù)用性好,Reactor 模型本身與具體事件處理邏輯無關(guān),具有很高的復(fù)用性

          雖然Reactor有諸多優(yōu)點(diǎn),但是由于他的IO讀寫數(shù)據(jù)時(shí)還是在同一個(gè)線程中實(shí)現(xiàn)的,如果當(dāng)前線程出現(xiàn)了一個(gè)長(zhǎng)時(shí)間的IO數(shù)據(jù)讀寫,則會(huì)影響其他的client。那怎么解決呢?請(qǐng)靜候下一篇文章。

          參考資料

          • https://blog.csdn.net/qq_34827674/article/details/116175772

          • https://blog.csdn.net/syc001/article/details/72841945


          瀏覽 55
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  中文字幕撸一撸 | 9·1樱桃免费观看网站 | 一级黄色视频直播 | 人人操人人看人人干 | 色偷偷伊人|