Netty4之IO模型及線程模型

本文是基於Netty4.1.x,Netty作爲異步事件驅動的網絡,高性能之處主要來自於其I/O模型和線程處理模型,前者決定如何收發數據,後者決定如何處理數據。

一、I/O模型

用什麼的通道將數據發送給對方,BIO(Blocking I/O)、NIO(Nonblocking I/O)或AIO,I/O模型在很大程度上決定了框架的性能。

  • 傳統阻塞型I/O(BIO)
     特點:每個請求都需要獨立的線程完成數據read,業務處理,數據write的完整操。
     問題:連接建立後,如果當前線程暫時沒有數據可讀,則線程就阻塞在read操作上,造成線程資源浪費;當併發數較大時,需要創建大量線程來處理連接,系統資源佔用較大。
  • I/O複用模型

在I/O複用模型中,會用到select,這個函數也會使進程阻塞,但是和阻塞I/O所不同的的,這兩個函數可以同時阻塞多個I/O操作,而且可以同時對多個讀操作,多個寫操作的I/O函數進行檢測,直到有數據可讀或可寫時,才真正調用I/O操作函數Netty的非阻塞I/O的實現關鍵是基於I/O複用模型,這裏用Selector對象表示:

           Netty的IO線程NioEventLoop由於聚合了多路複用器Selector,可以同時併發處理成百上千個客戶端連接。當線程從某客戶端Socket通道進行讀寫數據時,若沒有數據可用時,該線程可以進行其他任務。線程通常將非阻塞 IO 的空閒時間用於在其他通道上執行 IO 操作,所以單獨的線程可以管理多個輸入和輸出通道。

        由於讀寫操作都是非阻塞的,這就可以充分提升IO線程的運行效率,避免由於頻繁I/O阻塞導致的線程掛起,一個I/O線程可以併發處理N個客戶端連接和讀寫操作,這從根本上解決了傳統同步阻塞I/O一連接一線程模型,架構的性能、彈性伸縮能力和可靠性都得到了極大的提升。

二、線程模型Reactor(多路複用)

數據報如何讀取?讀取之後的編解碼在哪個線程進行,編解碼後的消息如何派發,線程模型的不同,對性能的影響也非常大。Netty主要是基於主從Reactors多線程模型並做了一定的修改。下面先看看基於Reactor的幾種線程模型。

1、Reactor單線程模型

 

Reactor單線程模型僅使用一個線程來處理所有的事情,包括客戶端的連接和到服務器的連接,以及所有連接產生的讀寫事件,這種線程模型需要使用異步非阻塞I/O,使得每一個操作都不會發生阻塞,Handler爲具體的處理事件的處理器,而Acceptor爲連接的接收者,作爲服務端接收來自客戶端的鏈接請求。這樣的線程模型理論上可以僅僅使用一個線程就完成所有的事件處理,顯得線程的利用率非常高,而且因爲只有一個線程在工作,所有不會產生在多線程環境下會發生的各種多線程之間的併發問題,架構簡單明瞭,線程模型的簡單性決定了線程管理工作的簡單性。但是因爲這樣也會存在一些缺點:

a、僅利用一個線程來處理事件,對於目前普遍多核的機器來說太過浪費資源;

b、一個線程同時處理N個連接,管理起來較爲複雜,而且性能也無法得到保證,這是以線程管理的簡潔換取來的事件管理的複雜性,而且是在性能無 法得到保證的前提下換取的,在大流量的應用場景下根本沒有實用性;

c、當處理的這個線程負載過重之後,處理速度會變慢,會有大量的事件堆積,甚至超時,而超時的情況下,客戶端往往會重新發送請求,這樣的情況下,這個單線程的模型就會成爲整個系統的瓶頸;

d、單線程模型的一個致命缺陷就是可靠性問題,因爲僅有一個線程在工作,如果這個線程出錯了無法正常執行任務了,那麼整個系統就會停止響應;

2、Reactor多線程模型

 

多線程模型下,接收鏈接和處理請求作爲兩部分分離了,而Acceptor使用單獨的線程來接收請求,做好準備後就交給事件處理的handler來處理,而handler使用了一個線程池來實現,這個線程池可以使用Executor框架實現的線程池來實現,所以,一個連接會交給一個handler線程來複雜其上面的所有事件,需要注意,一個連接只會由一個線程來處理,而多個連接可能會由一個handler線程來處理,關鍵在於一個連接上的所有事件都只會由一個線程來處理,這樣的好處就是消除了不必要的併發同步的麻煩。但是這樣的模式也會存在一些問題:

a、多線程模型下任然只有一個線程來處理客戶端的連接請求,那如果這個線程掛了,那整個系統任然會變爲不可用,

b、僅僅由一個線程來負責客戶端的連接請求,如果連接之後要做一些驗證之類複雜耗時操作再提交給handler線程來處理的話,就會出現性能問題

3、Reactor主從多線程模型(Netty線程模型的基礎)

 

Netty服務端啓動代碼如下:

Netty的服務端使用了兩個EventLoopGroup,而第一個EventLoopGroup通常只有一個EventLoop,通常叫做bossGroup,負責客戶端的連接請求,然後打開Channel,交給後面的EventLoopGroup中的一個EventLoop來負責這個Channel上的所有讀寫事件,一個Channel只會被一個EventLoop處理,而一個EventLoop可能會被分配給多個Channel來負責上面的事件,當然,Netty不僅支持NI/O,還支持OI/O,所以兩者的EventLoop分配方式有所區別,下面分別展示了NI/O和OI/O的分配方式:

 

 

 

 

在NI/O非阻塞模式下,Netty將負責爲每個Channel分配一個EventLoop,一旦一個EventLoop唄分配給了一個Channel,那麼在它的整個生命週期中都使用這個EventLoop,但是多個Channel將可能共享一個EventLoop,所以和Thread相關的ThreadLocal的使用就要特別注意,因爲有多個Channel在使用該Thread來處理讀寫時間。在阻塞IO模式下,考慮到一個Channel將會阻塞,所以不太可能將一個EventLoop共用於多個Channel之間,所以,每一個Channel都將被分配一個EventLoop,並且反過來也成立,也就是一個EventLoop將只會被綁定到一個Channel上來處理這個Channel上的讀寫事件。無論是非阻塞模式還是阻塞模式,一個Channel都將會保證一個Channel上的所有讀寫事件都只會在一個EventLoop上被處理。

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