引言
我一直認為對於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的底層就是使用的該模型。