1. 程式人生 > >學習Netty前對BIO、NIO、AIO的理解

學習Netty前對BIO、NIO、AIO的理解

概念

  • Netty是一個提供了易於使用的API的客戶端/伺服器框架
  • 高併發NIO(非阻塞IO)
  • 傳輸快,零拷貝(在Java中,記憶體分為堆疊常量池等。假設現在我們有一些資料,我們需要從IO裡面讀取並且放到堆裡面,那麼一般都會從IO流將資料讀入緩衝區,然後再從緩衝區裡讀到堆記憶體中。這樣的話,資料要經過兩次的拷貝才能到達最終的堆裡。如果資料很大的話,其實會造成資源的浪費。而零拷貝是指它會去開闢一個新的堆記憶體,然後資料直接從IO讀到新開闢的記憶體中,這樣子就加快了資料的傳輸速度。)

阻塞與非阻塞

執行緒訪問資源,該資源是否準備就緒的一種處理方式。
阻塞:執行緒A去請求一個資源,若該資源在處理中,它會一直等待,直到資源處理完畢。
非阻塞:執行緒B去請求一個資源,若該資源在處理中,它會去請求另外一個資源,若還是在處理中,就會一直去請求新的資源,而不會陷入等待。

同步與非同步

同步和非同步是指訪問資料的一種機制。
同步:執行緒A去請求一個資源,並等待資源的處理,資源處理完成後,會通知到執行緒A。
非同步:執行緒B請求一個資源,該資源在處理中,它會繼續請求另外一個資源,這兩個資源都處理完成後,會逐一以非同步的方式通知給執行緒B。

BIO(同步阻塞,Block IO)

在這裡插入圖片描述
原理:專門有一個執行緒(accept),負責監聽客戶端的請求。只要有客戶端和服務端建立了一個請求,那麼它們之間都會產生一個新的執行緒來處理。如果客戶端數量逐漸增多,那麼客戶端與伺服器之間就會頻繁的建立和銷燬執行緒。這時候服務端會面臨很大的壓力,甚至崩潰。在後續一段時間裡進行了改進,不再額外新增執行緒,而是使用執行緒池,這種方式其實也是一種偽非同步IO。

NIO(同步非阻塞,Non-Block IO)

在這裡插入圖片描述
原理:客戶端跟伺服器通訊時,伺服器會有一個多路複用器(Selector),也就是一個執行緒,它會去主動的輪詢。假設客戶端01與伺服器建立連線,會在多路複用器裡註冊,註冊完畢後產生一個通道(Channel)。每一個客戶端與伺服器建立連線都會產生一個與之對應的Channel。Channel是一個雙向通道,可以進行資料的讀寫,這些資料的讀寫都會進入到緩衝區中(Buffer)。Channel相當於是一個讀取的工具,每個客戶端都可以理解為一個單獨的Channel。讀取方式都是非阻塞的讀取,如果沒有資料會直接跳過而不會同步的在這邊乾等著。Selector是一個單執行緒,整體來說執行緒資源開銷很小,光這一個執行緒就能處理成千上萬的客戶端請求,客戶端的增多也不會影響它的效能。

BIO與NIO一個比較重要的不同是,我們使用BIO的時候往往會引入多執行緒,每個連線一個單獨的執行緒;而NIO則是使用單執行緒或者只使用少量的多執行緒,每個連線共用一個執行緒。
NIO的最重要的地方是當一個連線建立後,不需要對應一個執行緒,這個連線會被註冊到多路複用器上面,所以所有的連線只需要一個執行緒就可以搞定,當這個執行緒中的多路複用器進行輪詢的時候,發現連線上有請求的話,才開啟一個執行緒進行處理,也就是一個請求一個執行緒模式。
NIO使用了多路複用器機制,以socket使用來說,多路複用器通過不斷輪詢各個連線的狀態,只有在socket有流可讀或者可寫時,應用程式才需要去處理它,線上程的使用上,就不需要一個連線就必須使用一個處理執行緒了,而是隻是有效請求時(確實需要進行I/O處理時),才會使用一個執行緒去處理,這樣就避免了BIO模型下大量執行緒處於阻塞等待狀態的情景。

AIO(非同步非阻塞)

與NIO不同,當進行讀寫操作時,只須直接呼叫API的read或write方法即可。這兩種方法均為非同步的,對於讀操作而言,當有流可讀取時,作業系統會將可讀的流傳入read方法的緩衝區,並通知應用程式;對於寫操作而言,當作業系統將write方法傳遞的流寫入完畢時,作業系統主動通知應用程式。 即可以理解為,read/write方法都是非同步的,完成後會主動呼叫回撥函式。

生活例項

  • BIO:去上廁所,坑全滿。此時我一直光等著,主動觀察哪個坑好了,只要有坑位釋放了,我立馬去佔坑。
  • NIO:去上廁所,坑全滿。此時我回到工位上繼續寫程式碼,然後時不時再主動去看下廁所裡的人出來沒,如果有坑了,我就立馬去佔坑。
  • 非同步阻塞:去上廁所,坑全滿。我就在廁所裡等著別人出來跟我說我可以去佔坑了。(這種方式比較傻,別人都出來了,我當然就可以去佔坑了,犯不著還要別人告訴我。)
  • AIO:去上廁所,坑全滿。此時我回到工位上繼續寫程式碼,如果廁所裡有人出來了,他會主動跟我講,我可以去佔坑了。(非常高效)