Netty基礎系列(1) --linux網路I/O模型
引言
我一直認為對於java的學習,掌握基礎的價效比要遠遠高於使用框架,而基礎知識中對於網路相關知識的掌握也是重中之重。對於一個java程式來說,無論是工作中還是面試,對於Netty的掌握都是及其重要的。所以博主下定決心深度的學習一下Netty並且做下筆記與心得,供大家一起學習探討。
Netty的卓越之處在於它是一個高效能、非同步事件驅動的NIO框架,目前很多著名的開源框架都使用Netty作為底層的通訊框架,如Haddop、storm等。
好的廢話不多說,我們直接進入主題。
linux的五種網路 I/O 模型
Linux的核心將所有的外部裝置都看作一個檔案來操作。比如操作一個檔案的時候,linux會得到這個檔案的檔案描述符(fd),通過這個描述符來操作檔案。socket的讀寫儘管不是本地的檔案,但是Linux是通過一個類似檔案描述符,稱為socket描述符(socketfd)來操作網路資料的。描述符就是一個數字,它指向核心中的一個結構體(檔案的路徑,資料區等一些屬性)。
阻塞I/O模型
這是我們最最常見的I/O模型,例如我們平常編寫java程式所用的讀寫檔案都是阻塞的。什麼意思呢?就是當程式執行到讀/寫這一步操作的時候不會繼續往下執行程式碼,而是等到讀/寫的操作執行完畢。
套接字介面的情況則是,程序準備接受或傳送的資料的時候,,會向核心呼叫recvfrom()方法,這個方法會一直阻塞,直到資料包接受或傳送完畢,或者發生錯誤的時候才返回,在此期間一直等待。
非阻塞I/O模型
區別於阻塞I/O模型,當我們接受或傳送資料包的時候不會阻塞的等待,而是通過一個迴圈檢查套介面的狀態,如果快取區中沒有資料,當程序呼叫recvfrom()方法的時候,核心直接返回一個EWOULDBLOCK錯誤。當有資料來的時候,才繼續後續操作。
I/O複用模型
這個是Netty底層所使用的 I/O模型,類似於一個小區的物業,管理著所有住戶的快遞,當有快遞小哥來送快遞的時候快遞小哥不用一層一層爬到使用者家中,而是將快遞存放到物業。再由物業來通知使用者取快遞。
原理是Linux提供 select/poll方法,通過啟動一個程序來管理所有連線的描述符,通過順序掃描所有描述符是否為就緒狀態。但是這種方式有一個弊端,那就是同時管理的描述符有上限,一般來說最多支援1024個。所以linux在後來提供了pselect/epoll方法,區別於select/poll方法:
-
迴圈掃描的時候只掃描活躍的檔案,所支援的描述符管理上線為作業系統的最大檔案控制代碼數,比如1GB記憶體的機器數量大約是10萬個控制代碼左右,因此現在基本上都是使用的後者。
-
由於epoll每次都只是掃描活躍的socket,並且在大多數情況下,只有少部分的socket是活躍的,因此epoll效率會高很多,但是如果在一個高速的LAN環境下,epoll並不會比select/poll的效率高太多;相反相率可能還會稍稍降低。
-
每次接受資料的時候核心需要把資料從核心快取複製到使用者快取,這一步記憶體複製其實是很降低效率的,epoll是通過核心和使用者空間mmap同一塊記憶體來實現的。
-
epoll的API更加簡單。
訊號驅動I/O模型
首先開啟套介面訊號驅動,並通過系統呼叫sigaction執行一個訊號處理函式,然後程序繼續工作。當資料準備就緒的時候,就為該程序生成一個SIGIO訊號,通過訊號回撥通知引用程式來讀取資料。
非同步I/O模型
這種模型與訊號驅動模型的主要區別就是:訊號驅動I/O模型由核心通知程序可以開始一個I/O操作了。而非同步I/O模型這個逼就更猛了,直接通知程序,勞資已經幫你搞完了。
總結
這五種模型每一種都有很深的學問,博主在這裡只是用我自己所理解的知識來簡單的介紹一下這五種模型,但是如果你對其中的I/O模型有濃厚的興趣,可以自行對每一種模型進行進一步深度學習。這裡我推薦I/O複用模型,因為Netty的底層就是使用的該模型。