- Netty源碼剖析與應用
- 劉耀林
- 1681字
- 2021-01-05 18:23:46
第2章
原理部分
2.1 多路復用器
通過第1章的介紹,我們對Netty有了初步的了解,但距離完全掌握Netty,并編寫高性能的RPC服務還有一定的差距。本章主要對Netty和NIO的一些特性進行梳理,以了解它們的底層原理,為后面編寫RPC分布式服務打好理論基礎。
NIO有一個非常重要的組件——多路復用器,其底層有3種經典模型,分別是epoll、select和poll。與傳統I/O相比,一個多路復用器可以處理多個Socket連接,而傳統I/O對每個Socket連接都需要一條線程去同步阻塞處理。NIO有了多路復用器后,只需一條線程即可管理多個Socket連接的接入和讀寫事件。Netty的多路復用器默認調用的模型是epoll模型。它除了JDK自帶的epoll模型的封裝,還額外封裝了一套,它們都是epoll模型的封裝,只是JDK的epoll模型是水平觸發的,而Netty采用JNI重寫的是邊緣觸發。
2.1.1 NIO與BIO的區別
NIO為同步非阻塞I/O,BIO為同步阻塞I/O。阻塞I/O與非阻塞I/O的區別如下。
阻塞I/O:例如,客戶端向服務器發送10B的數據,若服務器一次只接收到8B的數據,則必須一直等待后面2B大小的數據的到來,在后面2B的數據未到達時,當前線程會阻塞在接收函數處。
非阻塞I/O:從TCP緩沖區中讀取數據,緩沖區中的數據可分多次讀取,不會阻塞線程。例如,已知后面將有10B的數據發送過來,但是如果現在緩沖區只收到8B的數據,那么當前線程就會讀取這8B的數據,讀完后立即返回,等另外2B的數據發來時再去讀取。NIO中的多路復用器管理成千上萬條的Socket連接。當多路復用器每次從TCP緩沖區讀數據時,若有些客戶端數據包未能全部到達,且讀取數據的線程是在阻塞的情況下,則只有全部數據到達時才能返回,這樣不僅性能弱,還不可控,無法預測等待的時間。
圖2-1為BIO服務器流程圖,圖2-2為NIO服務器整體流程圖。

圖2-1 BIO服務器流程圖

圖2-2 NIO服務器整體流程圖
從圖2-1和圖2-2中可以明顯地看出,NIO比BIO復雜得多,NIO主要是多了Selector。Selector能監聽多個Channel,當運行select()方法時,會循環檢測是否有就緒事件的Channel。只需一條線程即可管理多個Channel,而且對Channel的讀/寫采用的都是非阻塞I/O。與BIO相比,NIO同時接入的Channel會更多、資源利用率會更高。因為BIO的一條線程只能對單個Channel進行阻塞讀/寫,處理完后才能繼續接入并處理其他Channel,并發處理能力太弱。
2.1.2 epoll模型與select模型的區別
I/O多路復用器單個進程可以同時處理多個描述符的I/O,Java應用程序通過調用多路復用器來獲取有事件發生的文件描述符,以進行I/O的讀/寫操作。多路復用器常見的底層實現模型有epoll模型和select模型,本節詳細介紹它們各自的特點。
select模型有以下3個特點。
(1)select模型只有一個select函數,每次在調用select函數時,都需要把整個文件描述符集合從用戶態拷貝到內核態,當文件描述符很多時,開銷會比較大。
(2)每次在調用select函數時,內核都需要遍歷所有的文件描述符,這個開銷也很大,尤其是當很多文件描述符根本就無狀態改變時,也需要遍歷,浪費性能。
(3)select可支持的文件描述符有上限,可監控的文件描述符個數取決于sizeOf(fd_set)的值。如果sizeOf(fd_set)=512,那么此服務器最多支持512×8=4096個文件描述符。
epoll模型比select模型復雜,epoll模型有三個函數。第一個函數為int epoll_create(int size),用于創建一個epoll句柄。第二個函數為int epoll_ctl(int epfd,int op,int fd,struct epoll_event*event),其中,第一個參數為epoll_create函數調用返回的值;第二個參數表示操作動作,由三個宏(EPOLL_CTL_ADD表示注冊新的文件描述符到此epfd上,EPOLL_CTL_MOD表示修改已經注冊的文件描述符的監聽事件,EPOLL_CTL_DEL表示從epfd中刪除一個文件描述符)來表示;第三個參數為需要監聽的文件描述符;第四個參數表示要監聽的事件類型,事件類型也是幾個宏的集合,主要是文件描述符可讀、可寫、發生錯誤、被掛斷和觸發模式設置等。epoll模型的第三個函數為epoll_wait,表示等待文件描述符就緒。epoll模型與select模型相比,在以下這些地方進行了改善。
? 所有需要監聽的文件描述符只需在調用第二個函數int epoll_ctl時拷貝一次即可,當文件描述符狀態發生改變時,內核會把文件描述符放入一個就緒隊列中,通過調用epoll_wait函數獲取就緒的文件描述符。
? 每次調用epoll_wait函數只會遍歷狀態發生改變的文件描述符,無須全部遍歷,降低了操作的時間復雜度。
? 沒有文件描述符個數的限制。
? 采用了內存映射機制,內核直接將就緒隊列通過MMAP的方式映射到用戶態,避免了內存拷貝帶來的額外性能開銷。
了解這兩種多路復用器模型的特點主要是為了加深對NIO底層原理的理解,同時對深入了解Netty源碼有很大的幫助。
- Clojure Programming Cookbook
- Implementing VMware Horizon 7(Second Edition)
- JavaScript高效圖形編程
- Reactive Programming with Swift
- JavaScript+jQuery開發實戰
- Julia機器學習核心編程:人人可用的高性能科學計算
- Android程序設計基礎
- Android開發案例教程與項目實戰(在線實驗+在線自測)
- Learning SciPy for Numerical and Scientific Computing(Second Edition)
- 愛上micro:bit
- 21天學通C++(第5版)
- Azure Serverless Computing Cookbook
- ActionScript 3.0從入門到精通(視頻實戰版)
- SignalR:Real-time Application Development(Second Edition)
- Python繪圖指南:分形與數據可視化(全彩)