寸金難買寸光陰
Netty是⼀個⾼性能的、異步的、基於事件驅動的⽹絡應⽤框架
同步異步
如下是同步,同步異步是針對與一個請求本身來說的,如果需要給出相應,不給就的 等待,知道獲取相應,叫同步。
如下異步:
netty 基於java的nio,netty對各種傳輸類型,協議實現API進行了統一的封裝,實現了阻塞和非阻塞的Socket
⽐如:阿⾥巴巴的分佈式服務治理框架Dubbo,底層就是使⽤Netty作爲通信組件。
gRPC,是Google提供的⾼性能RPC框架,底層也使⽤了Netty。
Flink、Spark、Elasticsearch也採⽤了Netty作爲通信組件
RPC調用基本示意圖
基於此來實現電商系統中的訂單模塊的業務
這個通知是服務提供者在註冊中心註冊過了,告知調用者一下子
IO模型
BIO模型 NIO模型,AIO模型
java中的BIO是blocking I/O的簡稱,它是同步阻塞型IO,其相關的類和接⼝在java.io下。
BIO模型簡單來講,就是服務端爲每⼀個請求都分配⼀個線程進⾏處理
這種模式存在的問題:
客戶端的併發數與後端的線程數成1:1的⽐例,線程的創建、銷燬是⾮常消耗系統資源的,隨着併發量增⼤,服務端性能將顯著下降,甚⾄會發⽣線程堆棧溢出等錯誤。
當連接創建後,如果該線程沒有操作時,會進⾏阻塞操作,這樣極⼤的浪費了服務器資源。
NIO模型
non-block IO (⾮阻塞IO)
NIO相關的代碼都放在了java.nio包下,其三⼤核⼼組件:
Buffer(緩衝區)、Channel(通道)、Selector(選擇器/多路復⽤器)
Buffer
在NIO中,所有的讀寫操作都是基於緩衝區完成的,底層是通過數組實現的,常⽤的緩衝區是ByteBuffer,每⼀種java基本類型都有對應的緩衝區對象(除了Boolean類型),如:CharBuffer、IntBuffer、LongBuffer等。
Channel(和流對比,流單向,Channel雙向)
l在BIO中是基於Stream實現,⽽在NIO中是基於通道實現,與流不同的是,通道是雙向的,既可以讀也可以寫。
Selector
Selector是多路復⽤器,它會不斷的輪詢註冊在其上的Channel,如果某個Channel上發⽣讀或寫事件,這個Channel就處於就緒狀態,會被Selector輪詢出來,然後通過SelectionKey獲取就緒Channel的集合,進⾏IO的讀寫操作。
如下圖是NIO模型圖
解釋:chanel 是和server直接連的,單獨開了個線程Selector 來輪詢channel的變化
有變化,就selectorKey感知變化,進而處理請求。
可以看出,NIO模型要優於BIO模型,主要是:
通過多路復⽤器就可以實現⼀個線程處理多個通道,避免了多線程之間的上下⽂切換導致系統開銷過⼤。
NIO⽆需爲每⼀個連接開⼀個線程處理,並且只有通道真正有有事件時,才進⾏讀寫操作,這樣⼤⼤的減少了系統開銷。
AIO模型
在NIO中,Selector多路復⽤器在做輪詢時,如果沒有事件發⽣,也會進⾏阻塞
AIO是asynchronous I/O的簡稱,是異步IO,該異步IO是需要依賴於操作系統底層的異步IO實現。
AIO的基本流程是:⽤戶線程通過系統調⽤,告知kernel內核啓動某個IO操作,⽤戶線程返回。kernel內核在整個IO操作(包括數據準備、數據複製)完成後,通知⽤戶程序,⽤戶執⾏後續的業務操作。
發現還是回調,內核kernel 執行完成後,回調,告知用戶線程,完成了
輪詢優於回調的嚐盡個還有futuretask 和completablefuture
kernel的數據準備
將數據從⽹絡物理設備(⽹卡)讀取到內核緩衝區。
kernel的數據複製
將數據從內核緩衝區拷⻉到⽤戶程序空間的緩衝區。
這個有點像volatile 關鍵字,主存和線程內存關係,需要傳遞
⽬前AIO模型存在的不⾜:
需要完成事件的註冊與傳遞,這⾥邊需要底層操作系統提供⼤量的⽀持,去做⼤量的⼯作。
Windows 系統下通過 IOCP 實現了真正的異步 I/O。但是,就⽬前的業界形式來說,Windows 系統,很少作爲百萬級以上或者說⾼併發應⽤的服務器操作系統來使⽤。
⽽在 Linux 系統下,異步IO模型在2.6版本才引⼊,⽬前並不完善。所以,這也是在 Linux 下,實現⾼併發⽹絡編程時都是以 NIO 多路復⽤模型模式爲主。
Reactor線程模型
是⼀種併發編程模型,是⼀種思想
Netty就是結合了NIO的特點,應⽤了Reactor線程模型所實現的。
Reactor模型中定義的三種⻆⾊:
Reactor:負責監聽和分配事件,將I/O事件分派給對應的Handler。新的事件包含連接建⽴就緒、讀就緒、寫就緒等。
Acceptor:處理客戶端新連接,並分派請求到處理器鏈中。
Handler:將⾃身與事件綁定,執⾏⾮阻塞讀/寫任務,完成channel的讀⼊,完成處理業務邏輯後,負責將結果寫出channel。
常⻅的Reactor線程模型有三種,如下:
Reactor單線程模型
Reactor多線程模型
主從Reactor多線程模型
說明:
Reactor充當多路復⽤器⻆⾊,監聽多路連接的請求,由單線程完成
Reactor收到客戶端發來的請求時,如果是新建連接通過Acceptor完成,其他的請求由Handler完成。
Handler完成業務邏輯的處理,基本的流程是:Read --> 業務處理 --> Send 。
這種模型的優缺點:
優點
結構簡單,由單線程完成,沒有多線程、進程通信等問題。
適合⽤在⼀些業務邏輯⽐較簡單、對於性能要求不⾼的應⽤場景。
缺點
由於是單線程操作,不能充分發揮多核CPU的性能。
當Reactor線程負載過重之後,處理速度將變慢,這會導致⼤量客戶端連接超時,超時之後往往會進⾏重發,這更加重Reactor線程的負載,最終會導致⼤量消息積壓和處理超時,成爲系統的性能瓶頸。
可靠性差,如果該線程進⼊死循環或意外終⽌,就會導致整個通信系統不可⽤,容易造成單點故障。
單Reactor多線程模型
說明:
在Reactor多線程模型相⽐較單線程模型⽽⾔,不同點在於,Handler不會處理業務邏輯,只是負責響應⽤戶請求,真正的業務邏輯,在另外的線程中完成。
這樣可以降低Reactor的性能開銷,充分利⽤CPU資源,從⽽更專注的做事件分發⼯作了,提升整個應⽤的吞吐。
但是這個模型存在的問題:
多線程數據共享和訪問⽐較複雜。如果⼦線程完成業務處理後,把結果傳遞給主線程Reactor進⾏發送,就會涉及共享數據的互斥和保護機制。
Reactor承擔所有事件的監聽和響應,只在主線程中運⾏,可能會存在性能問題。例如併發百萬客戶端連接,或者服務端需要對客戶端握⼿進⾏安全認證,但是認證本身⾮常損耗性能。
爲了解決性能問題,產⽣了第三種主從Reactor多線程模型。
主從Reactor多線程模型
在主從模型中,將Reactor分成2部分:
MainReactor負責監聽server socket,⽤來處理⽹絡IO連接建⽴操作,將建⽴的socketChannel指定註冊給SubReactor。
SubReactor主要完成和建⽴起來的socket的數據交互和事件業務處理操作。
該模型的優點:
響應快,不必爲單個同步事件所阻塞,雖然Reactor本身依然是同步的。
可擴展性強,可以⽅便地通過增加SubReactor實例個數來充分利⽤CPU資源。
可復⽤性⾼,Reactor模型本身與具體事件處理邏輯⽆關,具有很⾼的復⽤性。