當前位置:
首頁 > 最新 > Netty-網路IO模型

Netty-網路IO模型

雖然目前的容器化微服務平台Avatar是基於k8s進行搭建,但對於小型私有雲部署來說,虛機的微服務方案依然有其存在的意義(私有雲方案常常是個位數Node的搭建,比如4~5個,在這上面部署一套K8完整方案,說實話是有點重型了),因此,對於Spring Cloud、Dubbo這類常用的基於虛機的微服務框架,也是需要經常去github上面關注一下其更新進展。

Avatar平台在正式上線的這段時間裡,有一半精力都花在定位網關和與之相關的網路問題上面(問題現象是時不時會出現超時的情況,最終通過優化網路流量模型得以徹底解決),在這過程中,我發現,無論是Spring Cloud的Zuul、Dubbo的底層通信框架、Hadoop的Avro,以及業界許多主流的RPC框架,底層都是基於Netty來構建,包括很多業務網關也常用netty+ajax來搭建。那麼,作為開發者,我們就很有必要來深入解析Netty。

說個題外話,上周面試了一位服務過國內很多著名ICT公司,做過省內著名互聯網公司CTO助理的一名高端架構師,可能是因為在面試過程中,我刨根問底地問了很多簡歷中提到的技術實現方案(如就近接入、灰度部署、分散式事務、多數據中心容災等等),應聘者有些惱羞成怒,拋下一句,「這些問題都問得太細節,我都不屑於回答」。好吧。。。可能是我的問題,但我認為,對技術細節和原理的掌握,也是一個架構師設計一個成功架構的基石之一。

一、我們先來介紹一下Linux網路IO模型

Linux的內核將所有外部設備都看做一個文件,操作時會調用內核命令並返回一個fd(file descriptor),我們稱之為文件描述符,是一個無符號整數。其數量取決於內存、int大小的定義和系統設定,一個進程的最大打開文件描述符可通過ulimit -n來查看,並且可以修改。而對一個socket的讀寫也會有相應的描述符,稱為socketfd。

根據Unix網路編程對於IO模型的分類,Unix提供了5種IO模型:

1、阻塞IO模型:在預設情況下,所有文件操作都是阻塞的。以socket為例,調用recvfrom,其內核調用直到數據包到達且被複制到buffer中或發生錯誤時才返回,在此期間一直會等待,進程在從調用revcfrom開始到它返回的整個期間內都是阻塞的。

2、非阻塞IO模型

recvfrom從應用層到內核的時候,如果buffer區沒有數據,就直接返回EWOULDBLOCK錯誤,需要應用輪循查詢buffer是否有數據。

3、IO復用模型

Linux提供select/poll,進程通過將一個或多個fd傳遞給select或poll調用,阻塞在select操作上,這樣select/poll可以偵測多個fd是否處於就緒狀態。select/poll順序掃描fd,Linux還提供了一個epoll,使用基於事件驅動方式代替順序掃描,性能會好於順序掃描。

4、信號驅動IO模型

首先創建socket,並通過sigaction執行信號處理函數,此系統調用立即返回,進程繼續工作。當數據準備就緒時,就為該進程生成一個SIGIO信號,通過信號回調通知應用程序調用recvfrom來讀取數據,並通知主循環函數處理數據。

5、非同步IO模型

它與信號IO模型的主要區別是:信號驅動IO由內核通知我們何時可以開始一個IO操作,非同步IO由內核通知我們IO操作何時已經準備好。

二、IO多路復用技術

當需要同時處理多個客戶端請求時,可用基於多線程或者IO多路復用技術進行處理。IO多路復用技術原理是通過將多個IO的阻塞復用到一個select的阻塞上,從而使得系統在單線程情況下可以同時處理多個客戶端請求。與傳統的多線程模型相比,最大的優勢是系統開銷小,系統不需要額外創建多個線程,也減少了線程之間的切換,提升系統總體的響應速度。

目前支持IO多路復用的有select、poll、epoll。但select存在一些限制:

1、比如單個進程打開的FD是有一定限制的,由FD_SETSIZE設置,默認值為1024,對於需要成千上成連接的大型服務來講,顯然比較少,雖然可以修改,但會帶來性能的下降。業界也常採用Apache的多進程方案,但如果涉及進程間同步數據,需要進程之間通過socket或共享內存同步,增加了編碼的複雜度。而epoll所支持的上限是OS的最大文件句柄數,一般遠遠大於1024。

2、select的另一個問題,是當socket較多時,每次掃描都會順序掃描全部集合,導致效率下降,特別是只有少數socket活躍的時候。而epoll是根據每個fd上的callback實現,效率就要高得多。

epoll只是一種實現方案,在freeBSD下有kqueue。

三、Netty的IO模型

在jdk 1.4之前,java只提供了BIO,當時的很長一段時間裡,大型伺服器都基於c++開發,因為c++可以基於OS能力自己設計和開發非同步I/O能力。1.4之後,java提供了NIO;1.7之後,java提供了AIO。

BIO:採用BIO模式的server,通常由一個獨立的Acceptor線程負責監聽client的連接,它接收到client連接請求之後,為每個client創建一個新的線程進行處理,處理完成之後,應答client。這個模型最大的問題就是缺少彈性伸縮能力,當client訪問增加時,server的線程個數和client並發的數量呈現1:1正比關係,最終導致創建新線程失敗,進程宕機。

NIO:所有的數據都是緩衝區處理,緩衝區是一個位元組數據ByteBuffer,包括讀寫。NIO提供了SocketChannel,網路請求通過channel進行讀寫,channel是全雙工的。selector多路復用器是NIO的基礎,它會不斷地輪詢註冊其上的Channel,如果某個Channel上面發生了讀或寫事件,這個Channel就處於就緒狀態,會被select輪詢出來,一個selector可以輪循多個channel。

一個典型的NIO server流程是:

1、當client的請求上來時,會建立一個channel與之對應並註冊到selector上面,並監聽accept事件。

2、selector輪循多個channel準備就緒的key。

3、若某個channel發生IO時,則將數據讀取到buffer中,並投遞到業務線程池中去處理。

Netty是基於BIO模型的,是業界最流行的NIO框架之一,若Servier需要實現NIO編程,建議直接基於Netty進行開發,盡量不要自己基於原生java NIO去實現。


喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


請您繼續閱讀更多來自 架構師的歷練 的精彩文章:

從架構師角度看區塊鏈-什麼是區塊鏈
互聯網運營平台通用架構設計

TAG:架構師的歷練 |