Netty淺析 - 1. 基礎
前言
在瞭解一個事物之前,最好能對它的基本屬性和相關概念有個基本的認知,所以學習Netty之前,也有必要了解與Netty相關的基礎概念知識;本篇將對Netty做一個基礎性的介紹,主要包括Netty的適用場景,特色以及基礎的IO知識,如果你已經瞭解這些知識,也可以跳過本篇,直接進入下一篇:Netty淺析 - 2. 實現
Netty是什麼?
首先我們來看Netty是什麼,關於這個問題,其 ofollow,noindex">官網 有一段闡述:
Netty is a NIO client server framework which enables quick and easy development of network applications such as protocol servers and clients
這段翻譯過來意思就是:
Netty是一個基於NIO的非同步網路程式設計框架,基於Netty能快速的搭建高效能易擴充套件的網路應用(包括客戶端與服務端)
具體來說Netty就是對於Java NIO的封裝,NIO又是什麼呢?NIO是Java 1.4後引入的基於 事件模型 的 非阻塞IO 框架,在NIO之前,對於資料的處理都是基於BIO(blocking IO),從名字上就知道BIO是以阻塞的形式對資料進行處理,這種處理形式比較簡單,但是既然阻塞的,那麼就不可避免會涉及到執行緒的操作,熟悉併發的小夥伴應該都知道,執行緒是一種昂貴的資源,無論是建立,銷燬,還是切換,這就導致BIO在面對一些特定場景如高併發等束手無策,而這些場景在網際網路應用中卻又很常見;對應的,NIO能較好的應對這些場景,遺憾的是,Java在剛推出NIO時,由於各種原因,致使其使用複雜,且經常會出現Bug,結果就是:廣大開發者有需求,但解決需求的工具就是不好用這樣尷尬的局面,怎麼辦呢? -- 自己動手,豐衣食足!大不了再造個"輪子",所以就出現了一系列解決NIO問題的框架,而Netty就是其中最著名的那一個(當然Java發展到現在,其NIO庫原本的很多問題都得到瞭解決,只不過很多解決方案借鑑的也是Netty的思想)
另外,Netty並不止於解決NIO的問題,它更進一步,還提供了一系列特色功能,具體請繼續往下看
Netty的特色
自己的孩子自己最瞭解,繼續來看Netty官網對於Netty特色的說(chui)明(niu):
It greatly simplifies and streamlines network programming such as TCP and UDP socket server
'Quick and easy' doesn't mean that a resulting application will suffer from a maintainability or a performance issue. Netty has been designed carefully with the experiences earned from the implementation of a lot of protocols such as FTP, SMTP, HTTP, and various binary and text-based legacy protocols. As a result, Netty has succeeded to find a way to achieve ease of development, performance, stability, and flexibility without a compromise
這段話大概的意思就是:
首先,Netty能極大的簡化你的網路程式設計;並且,簡單好用還不需要以複雜的管理和低效的效能為代價,Netty通過優秀的設計,在易部署,高效能,穩定性,擴充套件性之間找到了一個較好的平衡點
我們把這句話提煉出來,大概就可以得到Netty的幾大特色:
- 針對基本的需求提供了簡單易用的介面,直接上手!
- 針對複雜的場景提供了很強的擴充套件性,輕鬆應對業務發展!
- 在上面兩點的基礎上,效能不打折!
而如果對這些特點進行細化,則可以得出:
- 基於事件機制(Pipeline - Handler)達成關注點分離(訊息編解碼,協議編解碼,業務處理)
- 可定製的執行緒處理模型,單執行緒,多執行緒池等
- 遮蔽NIO本身的bug
- 效能上的優化
- 相較於NIO介面功能更豐富
- 對外提供統一的介面,底層支援BIO與NIO兩種方式自由切換
這些特性將在本系列第二篇裡做詳細分析;既然Netty的本質還是一個基於NIO的網路框架,那麼想要掌握Netty的精髓,對於NIO的瞭解就不可或缺
NIO處理模型介紹
在介紹NIO之前,最好了解一下BIO,還沒有學習過的小夥伴可以閱讀我另外一篇介紹BIO的文章: Java IO使用入門 -- IO其實很簡單
NIO是Java 1.4引入的一種同步非阻塞的I/O模型,也是I/O多路複用的基礎;相對於Java BIO(OIO)提供的基於面向流的阻塞式程式設計模型,NIO提供的是面向緩衝區的響應式事件程式設計模型

image.png
讀到這裡可能有些人會覺得迷糊,什麼阻塞?非阻塞?基於流?基於緩衝區?這裡有必要介紹一下Linux下的5中IO模型:

image.png
-
阻塞I/O模型:
最常用的I/O模型就是阻塞I/O模型,當用戶程序呼叫了recvfrom這個系統呼叫,kernel就開始了IO的第一個階段:準備資料(對於網路IO來說,很多時候資料在一開始還沒有到達。比如,還沒有收到一個完整的UDP包。這個時候kernel就要等待足夠的資料到來)。這個過程需要等待,也就是說資料被拷貝到作業系統核心的緩衝區中是需要一個過程的。而在使用者程序這邊,整個程序會被阻塞。當kernel一直等到資料準備好了,它就會將資料從kernel中拷貝到使用者記憶體,然後kernel返回結果,使用者程序才解除block的狀態,重新執行起來。 所以,blocking IO的特點就是在IO執行的兩個階段都被block了
image.png
-
非阻塞IO模型:
linux下,可以通過設定socket使其變為non-blocking。當對一個non-blocking socket執行讀操作時,流程是這個樣子:
image.png
-
IO複用模型:
IO multiplexing就是我們說的select,poll,epoll,有些地方也稱這種IO方式為event driven IO。select/epoll的好處就在於單個process就可以同時處理多個網路連線的IO。它的基本原理就是select,poll,epoll這個function會不斷的輪詢所負責的所有socket,當某個socket有資料到達了,就通知使用者程序
image.png
-
訊號驅動IO模型:
首先開啟套介面訊號驅動I/O功能,並通過系統呼叫sigaction執行一個訊號處理函式(此係統呼叫立即返回,程序繼續工作,它是非阻塞的)。當資料準備就緒時,就為該程序生成一個SIGIO訊號。隨即可以在訊號處理程式中呼叫recvfrom來讀資料,井通知主迴圈函式處理資料;一般用的較少
-
非同步IO:
在非同步IO模型下,使用者程序發起read操作之後,立刻就可以開始去做其它的事。而另一方面,從kernel的角度,當它受到一個asynchronous read之後,首先它會立刻返回,所以不會對使用者程序產生任何block。然後,kernel會等待資料準備完成,然後將資料拷貝到使用者記憶體,當這一切都完成之後,kernel會給使用者程序傳送一個signal,告訴它read操作完成了
image.png
介紹完這5種IO模型後,我們回到NIO,NIO基於的是IO複用模型(就是上面的第三種IO模型),正如在介紹IO複用模型時已提到,而在linux下,有三種針對該模型的實現,分別為:select,poll,epoll,其中epoll是linux 2.6後才有的;select有一個比較大的缺陷就是其程序所開啟的FD是有一定限制的,預設是1024,這個對於一些動輒需要上萬連線的大型伺服器來說,遠遠不夠用,當然這個值可以改大一些,但是一般情況把這個引數改大會影響效能(因為select是每次都迴圈遍歷所有的連線集合),而epoll是相關事件觸發後主動回撥,所以效能會好很多
總結
本篇主要介紹了Netty相關的基礎知識,主要為本系列的第二篇Netty淺析 - 2. 實現做準備,如果需要對IO模型進行更深入的瞭解,可以參考下面幾篇文章: