官术网_书友最值得收藏!

第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源碼有很大的幫助。

主站蜘蛛池模板: 六枝特区| 连平县| 霍山县| 宁海县| 荥经县| 庄浪县| 吉林省| 祁门县| 罗源县| 六枝特区| 井研县| 开江县| 桑日县| 石柱| 苏尼特右旗| 安化县| 丹凤县| 岐山县| 沙河市| 灵武市| 乃东县| 扶沟县| 枣阳市| 荥阳市| 莱西市| 乌拉特前旗| 宕昌县| 灵宝市| 温泉县| 盐边县| 乌拉特中旗| 南充市| 时尚| 黑龙江省| 容城县| 盐亭县| 乌海市| 德格县| 景宁| 阜阳市| 三穗县|