Netty框架總體架構及源碼知識點

Netty概述

Netty是一個高性能、異步事件驅動的NIO框架,它提供了對TCP、UDP和文件傳輸的支持。做爲當前最流行的NIO框架,Netty在互聯網領域、大數據分佈式計算領域、遊戲行業、通訊行業等得到了普遍的應用,一些業界著名的開源組件也基於Netty的NIO框架構建。html

Netty 利用 Java 高級網絡的能力,隱藏其背後的複雜性而提供一個易於使用的 API 構建一個客戶端/服務端,其具備高併發、傳輸快、封裝好等特色。react

高併發面試

Netty是一款基於NIO(Nonblocking I/O,非阻塞IO)開發的網絡通訊框架,對比於BIO(Blocking I/O,阻塞IO),他的併發性能獲得了很大提升 。數據庫

傳輸快編程

Netty的傳輸快其實也是依賴了NIO的一個特性——零拷貝。後端

封裝好安全

Netty封裝了NIO操做的不少細節,提供易於使用的API,還有心跳、重連機制、拆包粘包方案等特性,使開發者能可以快速高效的構建一個穩健的高併發應用。服務器

Netty框架總體架構及源碼知識點

image網絡

Netty框架

Netty項目致力於提供一個異步的、事件驅動的網絡應用框架和工具,用於快速開發可維護的、高性能的、高擴展性的服務器和客戶端之間的協議。換句話說,Netty式一個NIO客戶端服務器框架,可以快速、輕鬆地開發網絡應用例如服務器和客戶端間的協議。它簡化了網絡編程如TCP/IP socket服務器。多線程

JBOSSes Netty的設計吸收了大量的協議如FTP、SMTP、HTTP和各類二進制、基於文本的繼承協議等協議的設計經驗,成功地找到了一種方法實現易於開發、性能、穩定、靈活的協議開發。

特徵:

Netty爲用戶提供了不少創新和更好的網絡開發體驗。

1)設計Design

爲各類傳輸類型(塊和非塊socket)提供了統一的API;

創建在靈活和可擴展的事件模型;

高度可定製的線程模式——單線程,一個或多個線程池(如SEDA);

可信的五鏈接數據報socket支持。

2)易於使用

良好文檔化的Javadoc、用戶嚮導和例子;

結構並不臃腫;

無其它的依賴,只需JDK1.5或以上。

3)性能

高吞吐量、低延遲時間;

很小的資源消耗;

最小化沒必要要的內存複製。

4)健壯性

不會由於快速鏈接、慢速鏈接或超載鏈接引發OutOfMemoryError錯誤;

高速網絡下不會引發NIO程序的讀寫異常。

5)安全

徹底支持SSL/TLS和StartTLS;

在Java Applet環境下運行正常。

6)社區

至少每兩週一個版本發佈。

項目主頁: http://www.jboss.org/netty/

文檔地址: http://www.jboss.org/netty/documentation.html

下載地址: http://www.jboss.org/netty/downloads.html

爲何選擇Netty

Socket通訊(IO/NIO/AIO)編程,對於通訊模型已經有了一個基本的認識。咱們學習的僅僅是一個模型,若是想把這些真正的用於實際工做中,那麼還須要不斷的完善、擴展和優化。好比經典的TCP讀包寫包問題,或者是數據接收的大小,實際的通訊處理與應答的處理邏輯等等一些細節問題須要認真的去思考,而這些都須要大量的時間和經歷,以及豐富的經驗。因此想學好Socket通訊不是件容易事,那麼接下來就來學習一下新的技術Netty,爲何會選擇Netty?由於它簡單!使用Netty沒必要編寫複雜的邏輯代碼去實現通訊,不再須要去考慮性能問題,不須要考慮編碼問題,半包讀寫等問題。強大的Netty已經幫咱們實現好了,咱們只須要使用便可。

Netty是最流行的NIO框架,它的健壯性、功能、性能、可定製性和可擴展性在同類框架都是數一數二的。它已經獲得成百上千的商業/商用項目驗證,如Hadoop的RPC框架Avro、RocketMQ以及主流的分佈式通訊框架Dubbox等等。

Netty框架總體架構及源碼知識點

image

Netty的線程模型

併發系統能夠採用多種併發編程模型來實現。併發模型指定了系統中的線程如何經過協做來完成分配給它們的做業。不一樣的併發模型採用不一樣的方式拆分做業,同時線程間的協做和交互方式也不相同。

對於網絡請求通常能夠分爲兩個處理階段,一是接收請求任務,二是處理網絡請求。根據不一樣階段處理方式分爲如下幾種線程模型:

串行化處理模型

這個模型中用一個線程來處理網絡請求鏈接和任務處理,當worker接受到一個任務以後,就馬上進行處理,也就是說任務接受和任務處理是在同一個worker線程中進行的,沒有進行區分。這樣作存在一個很大的問題是,必需要等待某個task處理完成以後,才能接受處理下一個task。

而一般狀況下,任務的處理過程會比任務的接受流程慢得多。例如在處理任務的時候,咱們可能會須要訪問遠程數據庫,這屬於一種網絡IO。一般狀況下IO操做是比較耗時的,這直接影響了下一個任務的接受,並且一般在IO操做的時候,CPU是比較空閒的,白白浪費了資源。

所以能夠把接收任務和處理任務兩個階段分開處理,一個線程接收任務,放入任務隊列,另外的線程異步處理任務隊列中的任務。

並行化處理模型

因爲任務處理通常比較緩慢,會致使任務隊列中任務積壓長時間得不處處理,這時可使用多線程來處理。這裏使用的是一個公共的任務隊列,多線程環境中難免要經過加鎖來保證線程安全,咱們經常使用的線程池就是這種模式。能夠經過爲每一個線程維護一個任務隊列來改進這種模型。

Reactor線程模型

reactor線程模型關注的是:任務接受以後,對處理過程繼續進行切分,劃分爲多個不一樣的步驟,每一個步驟用不一樣的線程來處理,也就是本來由一個線程處理的任務如今由多個線程來處理,每一個線程在處理完本身的步驟以後,還須要將任務轉發到下階段線程繼續進行處理。

Netty的Reactor線程模型

其中mainReacotor,subReactor,Thread Pool是三個線程池。mainReactor負責處理客戶端的鏈接請求,並將accept的鏈接註冊到subReactor的其中一個線程上;subReactor負責處理客戶端通道上的數據讀寫;Thread Pool是具體的業務邏輯線程池,處理具體業務。

Netty具體線程模型

如何理解NioEventLoop和NioEventLoopGroup

1)NioEventLoop實際上就是工做線程,能夠直接理解爲一個線程。NioEventLoopGroup是一個線程池,線程池中的線程就是NioEventLoop。

2)實際上bossGroup中有多個NioEventLoop線程,每一個NioEventLoop綁定一個端口,也就是說,若是程序只須要監聽1個端口的話,bossGroup裏面只須要有一個NioEventLoop線程就好了。

每一個NioEventLoop都綁定了一個Selector,因此在Netty的線程模型中,是由多個Selecotr在監聽IO就緒事件。而Channel註冊到Selector。

一個Channel綁定一個NioEventLoop,至關於一個鏈接綁定一個線程,這個鏈接全部的ChannelHandler都是在一個線程中執行的,避免了多線程干擾。更重要的是ChannelPipline鏈表必須嚴格按照順序執行的。單線程的設計可以保證ChannelHandler的順序執行。

一個NioEventLoop的selector能夠被多個Channel註冊,也就是說多個Channel共享一個EventLoop。EventLoop的Selecctor對這些Channel進行檢查。

在監聽一個端口的狀況下,一個NioEventLoop經過一個NioServerSocketChannel監聽端口,處理TCP鏈接。後端多個工做線程NioEventLoop處理IO事件。每一個Channel綁定一個NioEventLoop線程,1個NioEventLoop線程關聯一個selector來爲多個註冊到它的Channel監聽IO就緒事件。NioEventLoop是單線程執行,保證Channel的pipline在單線程中執行,保證了ChannelHandler的執行順序。

小編準備了關於netty的面試題分享給你們,因爲文章篇幅緣由如下只分享10道netty的面試題。後五道題未設置答案,須要獲取答案和更多Java架構資料、面試題(含答案)和麪試心得以及視頻資料的能夠加入Java貓的架構學習基地:810589193獲取!

netty面試題

1.BIO、NIO和AIO的區別?

  • BIO:一個鏈接一個線程,客戶端有鏈接請求時服務器端就須要啓動一個線程進行處理。線程開銷大。
  • 僞異步IO:將請求鏈接放入線程池,一對多,但線程仍是很寶貴的資源。
  • NIO:一個請求一個線程,但客戶端發送的鏈接請求都會註冊到多路複用器上,多路複用器輪詢到鏈接有I/O請求時才啓動一個線程進行處理。
  • AIO:一個有效請求一個線程,客戶端的I/O請求都是由OS先完成了再通知服務器應用去啓動線程進行處理,
  • BIO是面向流的,NIO是面向緩衝區的;BIO的各類流是阻塞的。而NIO是非阻塞的;BIO的Stream是單向的,而NIO的channel是雙向的。
  • NIO的特色:事件驅動模型、單線程處理多任務、非阻塞I/O,I/O讀寫再也不阻塞,而是返回0、基於block的傳輸比基於流的傳輸更高效、更高級的IO函數zero-copy、IO多路複用大大提升了Java網絡應用的可伸縮性和實用性。基於Reactor線程模型。
  • 在Reactor模式中,事件分發器等待某個事件或者可應用或個操做的狀態發生,事件分發器就把這個事件傳給事先註冊的事件處理函數或者回調函數,由後者來作實際的讀寫操做。如在Reactor中實現讀:註冊讀就緒事件和相應的事件處理器、事件分發器等待事件、事件到來,激活分發器,分發器調用事件對應的處理器、事件處理器完成實際的讀操做,處理讀到的數據,註冊新的事件,而後返還控制權。

2.NIO的組成?

  • Buffer:與Channel進行交互,數據是從Channel讀入緩衝區,從緩衝區寫入Channel中的
  • flip方法 : 反轉此緩衝區,將position給limit,而後將position置爲0,其實就是切換讀寫模式
  • clear方法 :清除此緩衝區,將position置爲0,把capacity的值給limit。
  • rewind方法 : 重繞此緩衝區,將position置爲0
  • DirectByteBuffer可減小一次系統空間到用戶空間的拷貝。但Buffer建立和銷燬的成本更高,不可控,一般會用內存池來提升性能。直接緩衝區主要分配給那些易受基礎系統的本機I/O 操做影響的大型、持久的緩衝區。若是數據量比較小的中小應用狀況下,能夠考慮使用heapBuffer,由JVM進行管理。
  • Channel:表示 IO 源與目標打開的鏈接,是雙向的,但不能直接訪問數據,只能與Buffer 進行交互。經過源碼可知,FileChannel的read方法和write方法都致使數據複製了兩次!
  • Selector可以使一個單獨的線程管理多個Channel,open方法可建立Selector,register方法向多路複用器器註冊通道,能夠監聽的事件類型:讀、寫、鏈接、accept。註冊事件後會產生一個SelectionKey:它表示SelectableChannel 和Selector 之間的註冊關係,wakeup方法:使還沒有返回的第一個選擇操做當即返回,喚醒的緣由是:註冊了新的channel或者事件;channel關閉,取消註冊;優先級更高的事件觸發(如定時器事件),但願及時處理。
  • Selector在Linux的實現類是EPollSelectorImpl,委託給EPollArrayWrapper實現,其中三個native方法是對epoll的封裝,而EPollSelectorImpl. implRegister方法,經過調用epoll_ctl向epoll實例中註冊事件,還將註冊的文件描述符(fd)與SelectionKey的對應關係添加到fdToKey中,這個map維護了文件描述符與SelectionKey的映射。
  • fdToKey有時會變得很是大,由於註冊到Selector上的Channel很是多(百萬鏈接);過時或失效的Channel沒有及時關閉。fdToKey老是串行讀取的,而讀取是在select方法中進行的,該方法是非線程安全的。
  • Pipe:兩個線程之間的單向數據鏈接,數據會被寫到sink通道,從source通道讀取
  • NIO的服務端創建過程:Selector.open():打開一個Selector;ServerSocketChannel.open():建立服務端的Channel;bind():綁定到某個端口上。並配置非阻塞模式;register():註冊Channel和關注的事件到Selector上;select()輪詢拿到已經就緒的事件

3.Netty的特色?

  • 一個高性能、異步事件驅動的NIO框架,它提供了對TCP、UDP和文件傳輸的支持
  • 使用更高效的socket底層,對epoll空輪詢引發的cpu佔用飆升在內部進行了處理,避免了直接使用NIO的陷阱,簡化了NIO的處理方式。
  • 採用多種decoder/encoder 支持,對TCP粘包/分包進行自動化處理
  • 可以使用接受/處理線程池,提升鏈接效率,對重連、心跳檢測的簡單支持
  • 可配置IO線程數、TCP參數, TCP接收和發送緩衝區使用直接內存代替堆內存,經過內存池的方式循環利用ByteBuf
  • 經過引用計數器及時申請釋放再也不引用的對象,下降了GC頻率
  • 使用單線程串行化的方式,高效的Reactor線程模型
  • 大量使用了volitale、使用了CAS和原子類、線程安全類的使用、讀寫鎖的使用

4.Netty的線程模型?

  • Netty經過Reactor模型基於多路複用器接收並處理用戶請求,內部實現了兩個線程池,boss線程池和work線程池,其中boss線程池的線程負責處理請求的accept事件,當接收到accept事件的請求時,把對應的socket封裝到一個NioSocketChannel中,並交給work線程池,其中work線程池負責請求的read和write事件,由對應的Handler處理。
  • 單線程模型:全部I/O操做都由一個線程完成,即多路複用、事件分發和處理都是在一個Reactor線程上完成的。既要接收客戶端的鏈接請求,向服務端發起鏈接,又要發送/讀取請求或應答/響應消息。一個NIO 線程同時處理成百上千的鏈路,性能上沒法支撐,速度慢,若線程進入死循環,整個程序不可用,對於高負載、大併發的應用場景不合適。
  • 多線程模型:有一個NIO 線程(Acceptor) 只負責監聽服務端,接收客戶端的TCP 鏈接請求;NIO 線程池負責網絡IO 的操做,即消息的讀取、解碼、編碼和發送;1 個NIO 線程能夠同時處理N 條鏈路,可是1 個鏈路只對應1 個NIO 線程,這是爲了防止發生併發操做問題。但在併發百萬客戶端鏈接或須要安全認證時,一個Acceptor 線程可能會存在性能不足問題。
  • 主從多線程模型:Acceptor 線程用於綁定監聽端口,接收客戶端鏈接,將SocketChannel 從主線程池的Reactor 線程的多路複用器上移除,從新註冊到Sub 線程池的線程上,用於處理I/O 的讀寫等操做,從而保證mainReactor只負責接入認證、握手等操做;

5.TCP 粘包/拆包的緣由及解決方法?

  • TCP是以流的方式來處理數據,一個完整的包可能會被TCP拆分紅多個包進行發送,也可能把小的封裝成一個大的數據包發送。
  • TCP粘包/分包的緣由:
  • 應用程序寫入的字節大小大於套接字發送緩衝區的大小,會發生拆包現象,而應用程序寫入數據小於套接字緩衝區大小,網卡將應用屢次寫入的數據發送到網絡上,這將會發生粘包現象;
  • 進行MSS大小的TCP分段,當TCP報文長度-TCP頭部長度>MSS的時候將發生拆包
  • 以太網幀的payload(淨荷)大於MTU(1500字節)進行ip分片。
  • 解決方法
  • 消息定長:FixedLengthFrameDecoder類
  • 包尾增長特殊字符分割:行分隔符類:LineBasedFrameDecoder或自定義分隔符類 :DelimiterBasedFrameDecoder
  • 將消息分爲消息頭和消息體:LengthFieldBasedFrameDecoder類。分爲有頭部的拆包與粘包、長度字段在前且有頭部的拆包與粘包、多擴展頭部的拆包與粘包。

6.瞭解哪幾種序列化協議?

7.如何選擇序列化協議?

8.Netty的零拷貝實現?

9.Netty的高性能表如今哪些方面?

10.NIOEventLoopGroup源碼?

若是你想突破本身的天花板,那必定要比別人付出更多,這個過程是很辛苦的。若是你認準了一條路,堅持走下去,你必定會得到不少收穫和你滿意的答案。

相關文章
相關標籤/搜索