今天開始寫一些高並發實戰系列。
本系列主要講兩大主流框架:
Netty和Quasar(java纖程庫)
先介紹netty吧,netty是業界比較成熟的高性能異步NIO框架。
簡單來說,它就是對NIO2的封裝,但提供了更好用,bug更少的API。
為什么netty能提供高性能?核心要點有以下兩點:
1.Netty基於NIO2的事件驅動模式
2.零拷貝
先說,事件驅動模式吧,這個好理解,我們慢慢分解:
java原來IO操作都是阻塞的,一個IO請求一個線程,多個IO請求就要多個線程,很消耗資源。
現在NIO不一樣了,多個IO請求,一個線程(reactor),這個線程專門用來監聽不同事件(read,write,accept),根據事件類型分發到不同線程去異步處理,處理完后拿到結果返回給客戶端。原理如下圖:
舉個例子吧:比如你去KFC買漢堡,
1.如果是阻塞式IO的情景,是這樣的:
你:美女,給我來個雞腿堡,不要辣。
服務員小姜:好的,稍等。
然后服務員小姜,就去后台。先炸雞腿,再烤面包,再把雞腿放些沙拉醬,合上兩處面包,再用紙打個包。
最后,服務員小姜,一路小跑,到前台,把漢堡放在托盤上。
然后,小姜打開電腦跟你結賬:您好,一共19.9,謝謝。
2.如果是非阻塞式NIO的情景,是這樣的:
你:美女,給我來個雞腿堡,不要辣。
服務員小姜:好的,稍等。回頭一喊:老姜,給我來個雞腿堡。
然后服務員老姜,就去后台。先炸雞腿,再烤面包,再把雞腿放些沙拉醬,合上兩處面包,再用紙打個包。
最后,服務員老姜,一路小跑,到前台,把漢堡交給小姜,小姜把漢堡放在托盤上。
然后,小姜打開電腦跟你結賬:您好,一共19.9,送你一包薯條,謝謝。
這兩種情景,有什么不一樣?
有同學說,前面的情景,只有小姜一個人在干活,很着挺累人的。而且,貌似,小姜,同一時間,只能服務一個客戶。
也有同學說,后面加上老姜,小姜的工作輕松很多,也可以同進服務多個客戶,效率也提高了不少。
也有同學說,后面小姜,還送了一包薯條!-------好眼力,好細心!這都被你發現了!但這個不是問題的重點!年輕人不要分心啊!
這兩個場景,完全說明了一個問題:
阻塞式IO效率很低,等待時間長,吞吐量低;非阻塞式NIO效率高,等待時間短(后台可以用N個老姜),吞吐量高。
從上面的例子我們也可以很容易理解事件驅動模式。
什么叫事件驅動?
首先,要有事件:你要點個雞腿堡,這是個事件。
然后,這個事件被小姜“監聽”到了,這時,小姜的身份類似於NIO的selector(監聽器)。
小姜,一監聽到這個事件,立馬轉發給老姜(worker線程),老姜在后面忙活,然后沒有說話,扔給小姜一個:打包好的雞腿堡。
小姜再轉扔給你。
這就叫事件驅動模式。
現在我們再到說說什么是BIO,NIO,AIO,以及它們的區別和應用場景。
-
Java BIO : 同步並阻塞,服務器實現模式為一個連接一個線程,即客戶端有連接請求時服務器端就需要啟動一個線程進行處理,如果這個連接不做任何事情會造成不必要的線程開銷,當然可以通過線程池機制改善。
-
Java NIO : 同步非阻塞,服務器實現模式為一個請求一個線程,即客戶端發送的連接請求都會注冊到多路復用器上,多路復用器輪詢到連接有I/O請求時才啟動一個線程進行處理。
-
Java AIO(NIO.2) : 異步非阻塞,服務器實現模式為一個有效請求一個線程,客戶端的I/O請求都是由OS先完成了再通知服務器應用去啟動線程進行處理,
BIO、NIO、AIO適用場景分析:
-
BIO方式適用於連接數目比較小且固定的架構,這種方式對服務器資源要求比較高,並發局限於應用中,JDK1.4以前的唯一選擇,但程序直觀簡單易理解。
-
NIO方式適用於連接數目多且連接比較短(輕操作)的架構,比如聊天服務器,並發局限於應用中,編程比較復雜,JDK1.4開始支持。
-
AIO方式使用於連接數目多且連接比較長(重操作)的架構,比如相冊服務器,充分調用OS參與並發操作,編程比較復雜,JDK7開始支持。
另外,I/O屬於底層操作,需要操作系統支持,並發也需要操作系統的支持,所以性能方面不同操作系統差異會比較明顯。
在高性能的I/O設計中,有兩個比較著名的模式Reactor和Proactor模式,其中Reactor模式用於同步I/O,而Proactor運用於異步I/O操作。
好了,今天的內容就這些,明天繼續講零拷貝。
對了,點漢堡時,別忘了跟服務員多要包薯條,畢竟這是免費的!:)