76 netty零拷貝原理&netty線程模型原理

1 什麼是零拷貝 zery-copy

2,爲什麼kafka中使用 zery-copy

3, 零拷貝技術實現方案有哪些?

4,mmap 與 sendfile 之間的區別

5, Reactor 三種線程模型

6,單個reactor,單個 reactor 多線程,主從reactor多線程

7, netty 線程模型原理
內存池 netty線程模型源碼

傳統的讀取IO操作:
讀操作:
1,應用程序發起讀數據的操作,JVM會發起read()系統調用
2,這時候操作系統OS 會進行一次上下文切換(把用戶空間切換到訥河空間)
3.通過磁盤控制器把數據copy到內核緩衝區內,這裏就發生了一次DMA Copy
4, 然後內核將數據copy到用戶空間的應用緩衝區內,發生了一次CPU Copy
5, read 調用返回後,會在進行一次上下文切換(內核空間切換到用戶空間)

讀操作發送兩次數據拷貝、兩次上下文的切換。
寫操作

1、應用發起寫操作,OS進行一次上下文切換(從用戶空間切換爲內核空間)
2、並且把數據copy到內核緩衝區Socket Buffer,做了一次CPU Copy
3、內核空間再把數據copy到磁盤或其他存儲(網卡,進行網絡傳輸),進行了DMA Copy
4、寫入結束後返回,又從內核空間切換到用戶空間
兩次上下文切換 兩次數據拷貝

也就是說 socket客戶端與服務器端通訊可能會發生四次上下文切換 四次數據拷貝。

zero-copy 零拷貝:
1,減少cpu拷貝數據次數, 最好不要讓我們的cpu拷貝數據
2,減少cpu上下文切換 用戶與內核態之間的切換。

1,直接內存 使用dma控制器將本地硬盤的數據拷貝到內核態,可以減少一次數據拷貝。

傳統IO爲什麼非常慢:

服務器下載文件:
1,從本地硬盤讀取該文件;
2,在將該文件寫入到網卡里;

用戶與內核態之間的切換非常的頻繁,效率很低。
步驟:
1,從本地硬盤讀取該文件:
用戶空間發出read 方法到內核態,內核態讀取硬盤數據
用戶態切換到內核態,使用dma技術將硬盤的數據拷貝到內核態
內核態讀取到數據後,cpu從內核態將該數據拷貝到用戶態。

上下文切換2次 cpu數據拷貝1次 dma數據拷貝一次,將該文件流緩存到用戶空間

2,再將該文件寫入到網卡中

用戶發出write方法,將該數據寫入到網卡中
用戶態切換到內核態,使用cpu線程將該數據拷貝到內核態socket緩衝。
使用dma技術獎數據拷貝到網卡中。
內核態切換到用戶態。
上下文切換2次 cpu數據拷貝1次 dma數據拷貝一次
得出結論: 服務器下載 上下文切換4次 cpu數據拷貝2次 dma數據拷貝2次

mmap與sendfile零拷貝原理:

MMAP+write
read() 系統調用的過程中會把內核緩衝區的數據拷貝到用戶的緩衝區內,於是爲了減少這一步的開銷,我們可以使用mmap()替換read()系統調用函數,
mmap()系統調用函數會直接把內核緩衝區裏的數據(映射)到用戶空間,這樣,操作系統內核與用戶空間就不需要再進行任何的數據拷貝工作。


可以減少一次 內核空間拷貝數據到用戶空間,該方案也不是最理想的零拷貝實現方案。

Sendfile

LInux 內核 在2.1版本

可以直接把內核緩衝區裏的數據拷貝到socket緩衝區裏,不在拷貝到用戶態,這樣就只有2次上下文切換,和3次數據拷貝 ,但是這還不是真正的零拷貝技術

Linux 內核 在2.4版本
sendfile() 系統調用的過程發生了點變化
MA控制器就可以直接將內核緩衝區的數據拷貝到網卡的緩衝區內,此過程不需要將數據從操作系統內核緩衝區拷貝到socket 緩衝區中,這樣就減少了一次數據拷貝;這就是所謂的零拷貝(Zero-copy)技術:

因爲我們沒有在內存層面去拷貝數據,也就是說全程沒有通過CPu 來搬運數據,所以的數據都是通過DMA來進行運輸,總體來看可以吧文件傳輸的性能提高至少一倍以上。

零拷貝應用場景
1,kafka
2,Nginx
3,Spark
netty線程模型

事件驅動:
可以處理一個或者多個輸入源(one or more inputs)
通過Service Handler 同步的將輸入事件(Event
)採用多路複用分發給相應的Request handler(多個)處理。
單線程模型(單Reactor單線程)
多線程模型: (單Reactor 多線程)
主從多線程模型:(多Reactor 多線程)

傳統的IO 模型
單Reactor 單線程。



有操作都在同一個NIO線程處理,在這個單線程中要負責接收請求,處理IO,編解碼所有操作,相當於一個飯館只有一個人,同時負責前臺和後臺服務,效率低。

  1. 一個NIO線程同時處理成百上千的連接,性能上無法支撐,即便NIO線程的CPU負荷達到100%,也無法滿足海量消息的編碼、解碼、讀取和發送。
    2.當NIO線程負載過重之後,處理速度將變慢,這會導致大量客戶端連接超時,超時之後往往會進行重發,這更加重了NIO線程的負載,最終會導致大量消息積壓和處理超時,成爲系統的性能瓶頸。
    3.可靠性問題:一旦NIO線程意外跑飛,或者進入死循環,會導致整個系統通信模塊不可用,不能接收和處理外部消息,造成節點故障。
    使用場景:客戶端發送連接數量有限,且業務線程處理時間非常快 可以使用單個線程維護多個不同連接 ,代表有Redis。
Netty相關代碼
EventLoopGroup bossGroup = new NioEventLoopGroup(1);
ServerBootstrap bootstrap = new ServerBootstrap();


優點:
不需要上下文切換,不存在線程安全的問題
缺點:、1,只有一個線程處理,無法發揮cpu多核的效率
2,如果請求較多的情況下容易遇到瓶頸,
3,如果某種意外的情況下因線程終止了,導致整個系統木塊無法使用
適用於客戶端連接數量有一定的限制,業務處理時間非常快
單Reactor 多線程



相當於一個飯館有一個前臺負責接待,有很多服務員去做後面的工作,這樣效率就比單線程模型提高很多。

1.由一個Reactor線程-Acceptor線程用於監聽服務端,接收客戶端連接請求;
2.網絡I/O操作讀、寫等由Reactor線程池負責處理;
3.一個Reactor線程可同時處理多條鏈路,但一條鏈路只能對應一個Reactor線程,這樣可避免併發操作問題;
4.絕大多數場景下,Reactor多線程模型都可以滿足性能需求,但是,在極個別特殊場景中,一個Reactor線程負責監聽和處理所有的客戶端連接可能會存在性能問題。例如併發百萬客戶端連接,或者服務端需要對客戶端握手進行安全認證,但是認證本身非常損耗性能。因此,誕生了第三種線程模型;
Netty設置相關代碼

EventLoopGroup bossGroup = new NioEventLoopGroup(1);
EventLoopGroup workerGroup = new NioEventLoopGroup();
ServerBootstrap bootstrap = new ServerBootstrap();

創建一個線程的bossGroup 線程組,這個線程負責處理客戶端的連接請求,而workerGroup 默認使用處理器個數*2的線程數量來處理I/o操作這就是相當於Reactor的多線程模型
優點:
可以充分利用多線程多核處理能力,處理效率比單線程快
缺點:
對線程之間訪問數據,有可能遇到線程安全問題。

REactor 主從模型:
多線程模型的缺點在於併發量很高的情況下,只有一個REactor 單線程去處理是來不及的,就像飯館只有一個前臺接待很多客人也是不夠的。‘爲此需要使用主從線程模型。
主從線程模型: 一個線程池接收請求,一組線程池處理IO
1,服務端使用一個獨立的主Reactor 線程池來處理客戶端連接,黨服務端收到連接請求時,從主線程池裏隨機選擇有個Reactor 線程池來處理客戶端的連接
2,鏈路建立成功後,將新創建的SocketChannel 註冊到sub reactor 線程池的某個Reactor 線程上,由他處理後續的I/o操作。
1,mainReactor 負責監聽 server socket ,用來處理新連接的建立。將建立的socketChannel 指定給註冊給subreactor
2.subReactor維護自己的selector, 基於mainReactor 註冊的socketChannel多路分離IO讀寫事件,讀寫網 絡數據,對業務處理的功能,另其扔給worker線程池來完成。

通俗易懂理解爲:mainReactor爲老闆、只負責接口、subReactor負責前臺接待
也就是多個前臺接待。
比起多線程單 Rector 模型,它是將 Reactor 分成兩部分,mainReactor 負責監聽並 Accept新連接,然後將建立的 socket 通過多路複用器(Acceptor)分派給subReactor。subReactor 負責多路分離已連接的 socket,讀寫網絡數據;業務處理功能,其交給 worker 線程池完成。通常,subReactor 個數上可與 CPU 個數等同。
Netty線程模型
1.創建服務端的時候實例化了 2 個 EventLoopGroup。bossGroup 線程組實際就是 Acceptor 線程池,負責處理客戶端的 TCP 連接請求。workerGroup 是真正負責 I/O 讀寫操作的線程組。通過這裏能夠知道 Netty 是多 Reactor 模型。

2.ServerBootstrap 類是 Netty 用於啓動 NIO 的輔助類,能夠方便開發。通過 group 方法將線程組傳遞到 ServerBootstrap 中,設置 Channel 爲 NioServerSocketChannel,接着設置 NioServerSocketChannel 的 TCP 參數,最後綁定 I/O 事件處理類 ChildChannelHandler。
輔助類完成配置之後調用 bind 方法綁定監聽端口,Netty 返回 ChannelFuture,f.channel().closeFuture().sync() 對同步阻塞的獲取結果。

3.調用線程組 shutdownGracefully 優雅推出,釋放資源。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章