成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

深入Netty邏輯架構(gòu),從Reactor線程模型開始

開發(fā) 前端
本文從Reactor線程模型開始說起,到Netty如何用EventLoop實(shí)現(xiàn)Reactor線程模型。

[[405910]]

本文是Netty系列第6篇

上一篇文章我們從一個(gè)Netty的使用Demo,了解了用Netty構(gòu)建一個(gè)Server服務(wù)端應(yīng)用的基本方式。并且從這個(gè)Demo出發(fā),簡(jiǎn)述了Netty的邏輯架構(gòu)。

今天主要是深入學(xué)習(xí)下 邏輯架構(gòu) 中的EventLoop 和 EventLoopGroup,掌握Netty的線程模型,這是Netty最精髓的知識(shí)點(diǎn)之一。

本文預(yù)計(jì)閱讀時(shí)間約 「15分鐘」,將重點(diǎn)圍繞以下幾個(gè)問題展開:

1.什么是Reactor線程模型?

2.EventLoopGroup、EventLoop 怎么實(shí)現(xiàn)Reactor線程模型?

3.深入Netty的線程模型優(yōu)化

  • Netty3和Netty4的線程模型變化
  • 什么是Netty4線程模型的無鎖串行化

4.從線程模型看最佳實(shí)踐

先簡(jiǎn)單回顧下上一篇的邏輯架構(gòu)圖,看看EventLoop 和 EventLoopGroup是在什么位置。

1.什么是Reactor線程模型?

先來回顧下我們?cè)贜etty系列的第2篇介紹的I/O線程模型,包括BIO、NIO、I/O多路復(fù)用、信號(hào)驅(qū)動(dòng)IO、AIO。IO多路復(fù)用在Java中有專門的NIO包封裝了相關(guān)的方法。

前面的文章也說過,使用Netty而不是直接使用Java NIO包,就是因?yàn)镹etty幫我們封裝了許多對(duì)NIO包的使用細(xì)節(jié),做了許多優(yōu)化。

其中非常著名的,就是Netty的「Reactor線程模型」。

  • 前置知識(shí)如果還不太清楚,可以回頭看看前面幾篇文章:
  • 《沒搞清楚網(wǎng)絡(luò)I/O模型?那怎么入門Netty》
  • 《從網(wǎng)絡(luò)I/O模型到Netty,先深入了解下I/O多路復(fù)用》
  • 《從I/O多路復(fù)用到Netty,還要跨過Java NIO包》

Reactor模式 是一種「事件驅(qū)動(dòng)」模式。

「Reactor線程模型」就是通過 單個(gè)線程 使用Java NIO包中的Selector的select()方法,進(jìn)行監(jiān)聽。當(dāng)獲取到事件(如accept、read等)后,就會(huì)分配(dispatch)事件進(jìn)行相應(yīng)的事件處理(handle)。

如果要給 Reactor線程模型 下一個(gè)更明確的定義,應(yīng)該是:

  • Reactor線程模式 = Reactor(I/O多路復(fù)用)+ 線程池

其中Reactor負(fù)責(zé)監(jiān)聽和分配事件,線程池負(fù)責(zé)處理事件。

然后,根據(jù)Reactor的數(shù)量和線程池的數(shù)量,又可以將Reactor分為三種模型

  • 單Reactor單線程模型 (固定大小為1的線程池)
  • 單Reactor多線程模型
  • 多Reactor多線程模型 (一般是主從2個(gè)Reactor)

1.1 單Reactor單線程模型

Reactor內(nèi)部通過 selector 監(jiān)聽連接事件,收到事件后通過dispatch進(jìn)行分發(fā)。

  • 如果是連接建立的事件,通過accept接受連接,并創(chuàng)建一個(gè)Handler來處理連接后續(xù)的各種事件。
  • 如果是讀寫事件,直接調(diào)用連接對(duì)應(yīng)的Handler來處理,Handler完成 read => (decode => compute => encode) => send 的全部流程

這個(gè)過程中,無論是事件監(jiān)聽、事件分發(fā)、還是事件處理,都始終只有 一個(gè)線程 執(zhí)行所有的事情。

缺點(diǎn):

在請(qǐng)求過多時(shí),會(huì)無法支撐。因?yàn)橹挥幸粋€(gè)線程,無法發(fā)揮多核CPU性能。

而且一旦某個(gè)Handler發(fā)生阻塞,服務(wù)端就完全無法處理其他連接事件。

1.2 單Reactor多線程模型

為了提高性能,我們可以把復(fù)雜的事件處理handler交給線程池,那就可以演進(jìn)為 「單Reactor多線程模型」 。 

這種模型和第一種模型的主要區(qū)別是把業(yè)務(wù)處理從之前的單一線程脫離出來,換成線程池處理。

1)Reactor線程

通過select監(jiān)聽客戶請(qǐng)求,如果是連接建立的事件,通過accept接受連接,并創(chuàng)建一個(gè)Handler來處理連接后續(xù)的讀寫事件。這里的Handler只負(fù)責(zé)響應(yīng)事件、read和write事件,會(huì)將具體的業(yè)務(wù)處理交由Worker線程池處理。

只處理連接事件、讀寫事件。

2)Worker線程池

處理所有業(yè)務(wù)事件,包括(decode => compute => encode) 過程。

充分利用多核機(jī)器的資源,提高性能。

缺點(diǎn):

在極個(gè)別特殊場(chǎng)景中,一個(gè)Reactor線程負(fù)責(zé)監(jiān)聽和處理所有的客戶端連接可能會(huì)存在性能問題。例如并發(fā)百萬客戶端連接(雙十一、春運(yùn)搶票)

1.3 多Reactor多線程模型

為了充分利用多核能力,可以構(gòu)建兩個(gè) Reactor,這就演進(jìn)為 「主從Reactor線程模型」 。

1)主Reactor

主 Reactor 單獨(dú)監(jiān)聽server socket,accept新連接,然后將建立的 SocketChannel 注冊(cè)給指定的 從Reactor,

2)從Reactor

從Reactor 將連接加入到連接隊(duì)列進(jìn)行監(jiān)聽,并創(chuàng)建handler進(jìn)行事件處理。執(zhí)行事件的讀寫、分發(fā),把業(yè)務(wù)處理就扔給worker線程池完成。

3)Worker線程池

處理所有業(yè)務(wù)事件,充分利用多核機(jī)器的資源,提高性能。

輕松處理百萬并發(fā)。

缺點(diǎn):

實(shí)現(xiàn)比較復(fù)雜。

不過有了Netty,一切都變得簡(jiǎn)單了。

Netty幫我們封裝好了一切,可以快速使用主從Reactor線程模型(Netty4的實(shí)現(xiàn)上增加了無鎖串行化設(shè)計(jì)),具體代碼這里就不貼了,可以看看上一篇的Demo。

2. EventLoop、EventLoopGroup 怎么實(shí)現(xiàn)Reactor線程模型?

上面我們已經(jīng)了解了Reactor線程模型,了解了它的核心就是:

  • Reactor線程模式 = Reactor(I/O多路復(fù)用)+ 線程池

它的運(yùn)行模式包括四個(gè)步驟:

  • 連接注冊(cè):建立連接后,將channel注冊(cè)到selector上
  • 事件輪詢:selcetor上輪詢(select()函數(shù))獲取已經(jīng)注冊(cè)的channel的所有I/O事件(多路復(fù)用)
  • 事件分發(fā):把準(zhǔn)備就緒的I/O事件分配到對(duì)應(yīng)線程進(jìn)行處理
  • 事件處理:每個(gè)worker線程執(zhí)行事件任務(wù)

那這樣的模型在Netty中具體怎么實(shí)現(xiàn)呢?

這就需要我們了解下EventLoop和EventLoopGroup了。

2.1 EventLoop是什么

EventLoop 不是Netty獨(dú)有的,它本身是一個(gè)通用的 事件等待和處理的程序模型。主要用來解決多線程資源消耗高的問題。例如 Node.js 就采用了 EventLoop 的運(yùn)行機(jī)制。

那么,在Netty中,EventLoop是什么呢?

  • 一個(gè)Reactor模型的事件處理器。
  • 單獨(dú)一個(gè)線程。
  • 一個(gè)EventLoop內(nèi)部會(huì)維護(hù)一個(gè)selector和一個(gè)「taskQueue任務(wù)隊(duì)列」,分別負(fù)責(zé)處理 「I/O事件」 和 「任務(wù)」。

「taskQueue任務(wù)隊(duì)列」是多生產(chǎn)者單消費(fèi)者隊(duì)列,在多線程并發(fā)添加任務(wù)時(shí),可以保證線程安全。

「I/O事件」即selectionKey中的事件,如accept、connect、read、write等;

「任務(wù)」包括 普通任務(wù)、定時(shí)任務(wù)等。

  • 普通任務(wù):通過 NioEventLoop 的 execute() 方法向任務(wù)隊(duì)列 taskQueue 中添加任務(wù)。例如 Netty 在寫數(shù)據(jù)時(shí)會(huì)封裝 WriteAndFlushTask 提交給 taskQueue。
  • 定時(shí)任務(wù):通過調(diào)用 NioEventLoop 的 schedule() 方法向 定時(shí)任務(wù)隊(duì)列 scheduledTaskQueue 添加一個(gè)定時(shí)任務(wù),用于周期性執(zhí)行該任務(wù)(如心跳消息發(fā)送等)。定時(shí)任務(wù)隊(duì)列的任務(wù) 到了執(zhí)行時(shí)間后,會(huì)合并到 普通任務(wù) 隊(duì)列中進(jìn)行真正執(zhí)行。

一圖勝千言:

 

EventLoop單線程運(yùn)行,循環(huán)往復(fù)執(zhí)行三個(gè)動(dòng)作:

  • selector事件輪詢
  • I/O事件處理
  • 任務(wù)處理

2.2 EventLoopGroup是什么

EventLoopGroup比較簡(jiǎn)單,可以簡(jiǎn)單理解為一個(gè)“EventLoop線程池”。 


Tips:

監(jiān)聽一個(gè)端口,只會(huì)綁定到 BossEventLoopGroup 中的一個(gè) Eventloop,所以, BossEventLoopGroup 配置多個(gè)線程也無用,除非你同時(shí)監(jiān)聽多個(gè)端口。

2.3 具體實(shí)現(xiàn)

Netty可以通過簡(jiǎn)單配置,支持單Reactor單線程模型 、單Reactor多線程模型 、多Reactor多線程模型。

我們以 「多Reactor多線程模型」 為例,來看看Netty是如何通過EventLoop來實(shí)現(xiàn)的。

還是一圖勝千言:

我們結(jié)合Reactor線程模型的四個(gè)步驟來梳理一下:

1)連接注冊(cè)

master EventLoopGroup中有一個(gè)EventLoop,綁定某個(gè)特定端口進(jìn)行監(jiān)聽。

一旦有新的連接進(jìn)來觸發(fā)accept類型事件,就會(huì)在當(dāng)前EventLoop的I/O事件處理階段,將這個(gè)連接分配給slave EventLoopGroup中的某一個(gè)EventLoop,進(jìn)行后續(xù) 事件的監(jiān)聽。

2)事件輪詢

slave EventLoopGroup中的EventLoop,會(huì)通過selcetor對(duì)綁定到自身的channel進(jìn)行輪詢,獲取已經(jīng)注冊(cè)的channel的所有I/O事件(多路復(fù)用)。

當(dāng)然,EventLoopGroup中會(huì)有 多個(gè)EventLoop 運(yùn)行,各自循環(huán)處理。具體EventLoop數(shù)量是由 用戶指定的線程數(shù) 或者 默認(rèn)為核數(shù)的2倍。

3)事件分發(fā)

當(dāng)slave EventLoopGroup中的EventLoop獲取到I/O事件后,會(huì)在EventLoop的 I/O事件處理(processSelectedKeys) 階段分發(fā)給對(duì)應(yīng)ChannelPipeline進(jìn)行處理。

注意,仍然在當(dāng)前線程進(jìn)行串行處理

4)事件處理

在ChannelPipeline中對(duì)I/O事件進(jìn)行處理。

I/O事件處理完后,EventLoop在 任務(wù)處理(runAllTasks) 階段,對(duì)隊(duì)列中的任務(wù)進(jìn)行消費(fèi)處理。

至此,我們就能完全梳理清楚EventLoopGroup/EventLoop 和 Reactor線程模型的關(guān)系了。

咦,好像有什么地方不對(duì)勁?

沒錯(cuò),細(xì)心的朋友可能會(huì)發(fā)現(xiàn),slave EventLoopGroup中并不是

  • 一個(gè)selector + 線程池

而是有多個(gè)EventLoop組成的

  • 多selector + 多個(gè)單線程

這是為什么呢?

那就得繼續(xù)深入了解下Netty4的線程模型優(yōu)化了。

3. 深入Netty的線程模型優(yōu)化

上文說過,對(duì)每個(gè)EventLoop來說,都是單線程運(yùn)行,并循環(huán)往復(fù)執(zhí)行三個(gè)動(dòng)作:

  • selector事件輪詢
  • I/O事件處理
  • 任務(wù)處理

在slave EventLoopGroup中,并不是 “一個(gè)selector + 線程池”模式,而是有多個(gè)EventLoop組成的 “多selector + 多個(gè)單線程“ 模型,這是為什么呢?

這主要是因?yàn)槲覀兎治龅氖荖etty4的線程模型,跟Netty3的傳統(tǒng)Reactor模型相比有了不同之處。

3.1 Netty3和Netty4的線程模型變化

在Netty3的線程模型中,分為 讀事件處理模型 和 寫事件處理模型。

  • read事件的ChannelHandler都是由Netty的 I/O 線程(對(duì)應(yīng)Netty 4 中的 EventLoop)中負(fù)責(zé)執(zhí)行。
  • I/O線程調(diào)度執(zhí)行ChannelPipeline中Handler鏈的對(duì)應(yīng)方法,直到業(yè)務(wù)實(shí)現(xiàn)的End Handler。
  • End Handler將消息封裝成Runnable,放入到業(yè)務(wù)線程池中執(zhí)行,I/O線程返回,繼續(xù)讀/寫等I/O操作。

 

  • write事件是由調(diào)用線程處理,可能是 I/O 線程,也可能是業(yè)務(wù)線程。
  • 如果是業(yè)務(wù)線程,那么業(yè)務(wù)線程會(huì)執(zhí)行ChannelPipeline中的Channel Handler。
  • 執(zhí)行到系統(tǒng)最后一個(gè)ChannelHandler,將編碼后的消息Push到發(fā)送隊(duì)列中,業(yè)務(wù)線程返回。
  • Netty的I/O線程從發(fā)送消息隊(duì)列中取出消息,調(diào)用SocketChannel的write方法進(jìn)行消息發(fā)送。

由上文可以看到,在Netty3的線程模型中,是采用“selector + 業(yè)務(wù)線程池”的模型。

注意,在這種模型下,讀寫模型不一致。尤其是讀事件、寫事件的「執(zhí)行線程」是不一樣的。

但是在Netty4的線程模型中,采用了“多selector + 多個(gè)單線程”模型。

讀事件:

  • I/O線程N(yùn)ioEventLoop從SocketChannel中讀取數(shù)據(jù),將ByteBuf投遞到ChannelPipeline,觸發(fā)ChannelRead事件;
  • I/O線程N(yùn)ioEventLoop調(diào)用ChannelHandler鏈,直到將消息投遞到業(yè)務(wù)線程,然后I/O線程返回,繼續(xù)后續(xù)的操作。

寫事件:

  • 業(yè)務(wù)線程調(diào)用ChannelHandlerContext.write(Object msg)方法進(jìn)行消息發(fā)送。
  • ChannelHandlerInvoker將發(fā)送消息封裝成 任務(wù),放入到EventLoop的Mpsc任務(wù)隊(duì)列中,業(yè)務(wù)線程返回。后續(xù)由EventLoop在循環(huán)中統(tǒng)一調(diào)度和執(zhí)行。
  • I/O線程EventLoop在進(jìn)行 任務(wù)處理 時(shí),從Mpsc任務(wù)隊(duì)列中獲取任務(wù),調(diào)用ChannelPipeline進(jìn)行處理,處理Outbound事件,直到將消息放入發(fā)送隊(duì)列,然后喚醒Selector,執(zhí)行寫操作。

Netty4中,無論讀寫,都是通過I/O線程(也就是EventLoop)來統(tǒng)一處理。

為什么Netty4的線程模型做了這樣的變化?答案就是 無鎖串行化設(shè)計(jì)。

3.2 什么是Netty4線程模型的無鎖串行化

我們先看看Netty3的線程模型存在什么問題:

讀/寫線程模型 不一致,帶來額外的開發(fā)心智負(fù)擔(dān)。

寫操作由業(yè)務(wù)線程發(fā)起時(shí),通常業(yè)務(wù)會(huì)使用 線程池多線程并發(fā)執(zhí)行 某個(gè)業(yè)務(wù)流程,所以某一個(gè)時(shí)刻會(huì)有多個(gè)業(yè)務(wù)線程同時(shí)操作ChannelHandler,我們需要對(duì)ChannelHandler進(jìn)行并發(fā)保護(hù),大大降低了開發(fā)效率。

頻繁的線程上下文切換,會(huì)帶來額外的性能損耗。

而Netty4線程模型的 「無鎖串行化」設(shè)計(jì),就很好地解決了這些問題。

一圖勝千言:

從事件輪詢、消息的讀取、編碼以及后續(xù)Handler的執(zhí)行,始終都由I/O線程N(yùn)ioEventLoop內(nèi)部進(jìn)行串行操作,這就意味著整個(gè)流程不會(huì)進(jìn)行線程上下文的切換,避免多線程競(jìng)爭(zhēng)導(dǎo)致的性能下降,數(shù)據(jù)也不會(huì)面臨被并發(fā)修改的風(fēng)險(xiǎn)。

表面上看,串行化設(shè)計(jì)似乎CPU利用率不高,并發(fā)程度不夠。但是,通過調(diào)整slave EventLoopGroup的線程參數(shù),可以同時(shí)啟動(dòng)多個(gè)NioEventLoop,串行化的線程并行運(yùn)行,這種局部無鎖化的串行線程設(shè)計(jì)相比「一個(gè)隊(duì)列-多個(gè)工作線程模型」性能更優(yōu)。

總結(jié)下Netty4無鎖串行化設(shè)計(jì)的優(yōu)點(diǎn):

  • 一個(gè)EventLoop會(huì)處理一個(gè)channel全生命周期的所有事件。從消息的讀取、編碼以及后續(xù)Handler的執(zhí)行,始終都由I/O線程N(yùn)ioEventLoop負(fù)責(zé)。
  • 每個(gè)EventLoop會(huì)有自己獨(dú)立的任務(wù)隊(duì)列。
  • 整個(gè)流程不會(huì)進(jìn)行線程上下文的切換,數(shù)據(jù)也不會(huì)面臨被并發(fā)修改的風(fēng)險(xiǎn)。
  • 對(duì)于用戶而言,統(tǒng)一的讀寫線程模型,也降低了使用的心智負(fù)擔(dān)。

4. 從線程模型看最佳實(shí)踐

NioEventLoop 無鎖串行化的設(shè)計(jì)這么好,它就完美無缺了嗎?

不是的!

在特定的場(chǎng)景下,Netty3的線程模型可能性能更高。比如編碼和其它寫操作非常耗時(shí),由多個(gè)業(yè)務(wù)線程并發(fā)執(zhí)行,性能肯定高于單個(gè)EventLoop線程串行執(zhí)行。

因此,雖然單線程執(zhí)行避免了線程切換,但是它的缺陷就是不能執(zhí)行時(shí)間過長(zhǎng)的 I/O 操作,一旦某個(gè) I/O 事件發(fā)生阻塞,那么后續(xù)的所有 I/O 事件都無法執(zhí)行,甚至造成事件積壓。

所以,Netty4的線程模型的最佳實(shí)踐需要注意以下兩點(diǎn):

  • 無論讀/寫,不在自定義ChannelHandler中做耗時(shí)操作。
  • 不把耗時(shí)操作放進(jìn) 任務(wù)隊(duì)列。

本文從Reactor線程模型開始說起,到Netty如何用EventLoop實(shí)現(xiàn)Reactor線程模型。

然后對(duì)Netty4的線程模型優(yōu)化做了詳細(xì)介紹,尤其是「無鎖串行化設(shè)計(jì)」。

最后從EventLoop線程模型出發(fā),說明了日常開發(fā)中使用Netty4開發(fā)的最佳實(shí)踐。

希望大家能對(duì)EventLoop有全面的認(rèn)識(shí)。

 

責(zé)任編輯:姜華 來源: 阿丸筆記
相關(guān)推薦

2022-09-29 15:39:10

服務(wù)器NettyReactor

2025-05-08 10:25:00

Netty網(wǎng)絡(luò)編程框架

2023-05-23 08:01:10

Netty網(wǎng)絡(luò)通信

2022-01-04 11:11:32

Redis單線程Reactor

2021-07-10 08:04:07

Reactor模式Netty

2024-10-24 20:48:04

Netty線程Java

2020-10-14 08:50:38

搞懂 Netty 線程

2022-03-06 12:15:38

NettyReactor線程

2022-03-10 07:58:12

ReactorNetty運(yùn)轉(zhuǎn)架構(gòu)

2022-03-04 08:10:35

NettyIO模型Reactor

2018-05-16 09:26:41

基線模型機(jī)器學(xué)習(xí)AI

2020-12-21 08:42:40

NettyByteBuf網(wǎng)絡(luò)技術(shù)

2021-02-10 08:09:48

Netty網(wǎng)絡(luò)多路復(fù)用

2024-05-31 08:10:58

Netty線程模型多路復(fù)用模型

2023-10-19 11:12:15

Netty代碼

2024-12-26 00:46:25

機(jī)器學(xué)習(xí)LoRA訓(xùn)練

2024-11-26 09:33:44

2023-06-24 19:59:40

2023-12-05 17:44:24

reactor網(wǎng)絡(luò)

2024-04-18 09:34:28

Reactor項(xiàng)目異步編程
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)

主站蜘蛛池模板: 91中文字幕在线 | www.国产| 日韩一区二区三区视频在线观看 | 欧美午夜视频 | 久久在线看 | 久久久夜 | 日本精品一区 | 国产精品久久久免费 | 国产精品日日摸夜夜添夜夜av | 国产成人高清在线观看 | 成人久久久 | 国产资源在线播放 | 午夜精品一区二区三区在线观看 | 日日天天 | 亚洲成人毛片 | 国产精品久久久久久久久免费丝袜 | 二区成人 | 欧美亚洲视频 | 国产精品久久一区二区三区 | 精品国产乱码久久久久久丨区2区 | 蜜桃臀av一区二区三区 | 国内精品伊人久久久久网站 | 日韩一| 久久久黑人 | 91精品国产91久久综合桃花 | 欧美成人精品一区二区男人看 | 国产视频中文字幕 | 亚洲国产成人精品久久 | 黄毛片| 久久久91精品国产一区二区三区 | 在线日韩欧美 | 久久亚洲欧美日韩精品专区 | 性欧美精品一区二区三区在线播放 | 欧美一级欧美三级在线观看 | 蜜桃视频在线观看免费视频网站www | 91在线免费观看网站 | 国产精品久久久久久一区二区三区 | 国产免费一区二区三区免费视频 | 福利视频网 | 欧美日韩在线电影 | 国产乱码精品一区二区三区中文 |