Netty
Netty是一款非常優(yōu)秀的網(wǎng)絡編程框架,是對NIO的二次封裝,本文將重點剖析Netty客戶端的啟動流程,深入底層了解如何使用NIO編程客戶端。
Linux網(wǎng)絡編程5種IO模型
根據(jù)UNIX網(wǎng)絡編程對于IO模型的分類,UNIX提供了5種IO模型,分別是 **阻塞IO 、 非阻塞IO、 IO復用 、 信號驅動IO 、 異步IO **。這幾種IO模型在《UNIX網(wǎng)絡編程》中有詳解,這里作者只簡單介紹,幫助大家回憶一下這幾種模型。
對于Linux來說,所有的操作都是基于文件的,也就是我們非常熟悉的fd,在缺省的情況下,基于文件的操作都是 阻塞的 。下面就通過系統(tǒng)調(diào)用 recvfrom 來回顧下這五種模型。模型的圖示來源于《Netty權威指南》下面不再說明。
系統(tǒng)調(diào)用 recvfrom 直到有數(shù)據(jù)到達并且從內(nèi)核空間copy到用戶空間才返回,這期間recvfrom調(diào)用者一直等待,這就是阻塞IO。
非阻塞IO
應用進程反復調(diào)用 recvfrom 詢問操作系統(tǒng)內(nèi)核數(shù)據(jù)是否準備好,相對于阻塞來說,你可以理解為非阻塞自主能力變強。從圖中可以看出數(shù)據(jù)沒有準備好的時候內(nèi)核返回 EWOULDBLOCK。
Linux給我們提供的IO復用相關的函數(shù)有 select 、 poll 、 epoll 這幾個函數(shù)的優(yōu)缺點這里就不做詳述了,想要再深入了解的同學可以看下我以前的這篇文章: epoll詳解:從底層了解IO復用 。
[圖片上傳失敗...(image-3ac91d-1611911588970)]
信號驅動IO
應用進程建立一個 SIGIO信號 處理程序,當內(nèi)核數(shù)據(jù)準備好的時候,會產(chǎn)生一個 SIGIO信號,處理程序收到這個信號并通知進程調(diào)用 recvfrom 。
應用程序執(zhí)行系統(tǒng)調(diào)用,告知內(nèi)核某個操作,并讓內(nèi)核在整個操作完成后(包括將數(shù)據(jù)從內(nèi)核復制到應用緩沖區(qū))通知應用進程。這種模式與信號驅動模式的主要區(qū)別在于:信號驅動IO由內(nèi)核通知我們何時可以開始IO操作。異步驅動IO由內(nèi)核通知我們IO操作是否完成。
[圖片上傳失敗...(image-ec3d4f-1611911588970)]
用漫畫幫你分清同步異步阻塞非阻塞
說到 同步 、 異步 、 阻塞 、 非阻塞 很多人分不清他們之間的區(qū)別,每次聽到這幾個詞語的時候就頭大。更別說他們的組合 同步阻塞 、 同步非阻塞 、 異步阻塞 、 異步非阻塞 了。針對這幾個大家非常容易混淆的詞語,我畫了一幅漫畫幫助大家理解。歡迎大家來吐槽我的第一個漫畫作品《洗衣服的故事》。
[圖片上傳失敗...(image-2e9b-1611911588969)]
[圖片上傳失敗...(image-df4ec9-1611911588969)]
你看到這四個詞語的時候肯定以為是說的同一件事,描述的是同一個對象,這種理解是錯誤的。我們可以把 同步、異步 看做一組, 阻塞和非阻塞 看成一組。這兩者的主要區(qū)別就是:前者是" 消息通知機制 ",后者是“ 等到消息通知時的狀態(tài) ”。
這樣說你可能還是沒有理解,結合上面的漫畫情景中"消息通知機制"就是洗衣機,“等待消息通知時的狀態(tài)"就是人。拿"異步非阻塞"舉例,洗衣機洗完衣服會發(fā)出"滴滴"聲通知人已經(jīng)完成了"洗衣服"這個任務,這就是"消息通知機制”,這個過程中人不必傻等著洗衣機"洗衣服"這個任務的結果,可以干其他的事情“澆花”,聽到洗衣機的"滴滴"聲之后可以去"晾衣服"。這就是一種"等待消息通知時的狀態(tài)"。
從BIO到AIO
BIO通信模型
BIO通信模型也叫 一請求一應答 模型。顧名思義,服務器每接收到一個請求就會生成一個線程來處理這個請求。
這種模式最大的問題是缺乏彈性伸縮能力。這種模式接收一個請求就開啟一個線程去處理。眾所周知,線程資源對于JVM虛擬機是極其寶貴的,線程數(shù)過度膨脹的時候,系統(tǒng)性能會急劇下降,并發(fā)很大的情況下很容易會導致資源耗盡而出現(xiàn)堆棧溢出,進而出現(xiàn)宕機或者僵死、服務器崩潰,無法對外服務。上面圖示中Client5就是資源耗盡時服務器無法提供服務的場景,用戶看到的將是異常或者超時。
為異步IO模型
用過線程池的同學都知道線程池的好處是我們可以指定 核心線程數(shù) 、 最大線程數(shù) 、 線程池隊列大小 并且可以設置 拒絕策略 。所以這種模式相比于阻塞IO來說線程的創(chuàng)建,服務的范圍是 可控的 。無論多少個客戶端并發(fā),都不會導致服務器資源的耗盡和宕機。
NIO編程
NIO是 Non-block I/O(非阻塞IO) 的簡稱。使用IO復用編寫的服務器和客戶端就是NIO編程的很好的示例。我們設置了讀寫都是非阻塞之后,當沒有可讀或者可寫的數(shù)據(jù)的時候,線程不同步等待,會直接返回。想要再深入了解的同學可以看下我以前的這篇文章: epoll詳解:從底層了解IO復用 。
AIO編程
NIO 2.0引入了新的異步通道的概念,并提供了異步文件通道和異步套接字通道的實現(xiàn)。
異步通道獲取獲取操作結果方式:
1.使用 java.util.concurrent.Future類 表示異步操作的結果;
2.在執(zhí)行異步操作的時候傳入一個 java.nio.channels 。
操作完成后胡回調(diào) CompletionHandler 接口的實現(xiàn)類。
NIO 2.0的異步套接字通道是真正的異步非阻塞I/O,對應于UNIX網(wǎng)絡編程中的事件驅動I/O。
為什么選擇Netty
不選擇Java原生NIO編程的原因
以下內(nèi)容引用自《Netty權威指南》:
- NIO類庫和API繁雜,使用麻煩,你需要熟練掌握 Selector , ServerSocketChannel、 SocketChannel 、 ByteBuffer 等。
- 需要其他額外的技能做鋪墊,例如熟悉Java多線程編程。這是因為NIO編程涉及Reactor模式,你必須對多線程和網(wǎng)絡編程十分熟悉,才能編寫出高質(zhì)量的NIO程序。
- 可靠性能力補齊 ,工作量和難度都非常大。例如客戶端面臨斷連重連、網(wǎng)絡閃斷、半包讀寫、失敗緩存、網(wǎng)絡擁塞和異常碼流的處理問題,NIO編程的特點是功能開發(fā)相對容易,可靠性能力補齊的工作量和難度巨大。
- JDK NIO的BUG,例如臭名昭著的 epoll GUG ,它會導致Selector空輪詢,最終導致CPU 100%。官方聲稱在1.6版本中修復,但是在1.7版本中該問題依然存在,只不過GUG發(fā)生的概率降低了很多。
為什么選擇Netty
Netty是業(yè)界最流行的NIO框架之一, 它的健壯性、功能、性能、可定制性和可擴展性在同類框架中都是首屈一指的, 它已經(jīng)得到成百上千的商用項目驗證, 例如Hadoop的RPC框架Avro就使用了Netty作為底層通信框架, 其他還有業(yè)界主流的RPC框架, 也使用Netty來構建高性能的異步通信能力。
通過對Netty的分析,我們將它的優(yōu)點總結如下。
- API使用簡單, 開發(fā)門檻低;
- 功能強大,預置了多種編解碼功能,支持多種主流協(xié)議;
- 定制能力強, 可以通過 ChannelHandler 對通信框架進行靈活地擴展;
- 性能高, 通過與其他業(yè)界主流的NIO框架對比,Netty的綜合性能最優(yōu);
- 成熟、穩(wěn)定,Netty修復了已經(jīng)發(fā)現(xiàn)的所有 JDK NIO BUG , 業(yè)務開發(fā)人員不需要再為NIO的BUG而煩惱;
- 社區(qū)活躍, 版本迭代周期短, 發(fā)現(xiàn)的BUG可以被及時修復,同時, 更多的新功能會加入;
- 經(jīng)歷了大規(guī)模的商業(yè)應用考驗, 質(zhì)量得到驗證。Netty在互聯(lián)網(wǎng)、大數(shù)據(jù)、網(wǎng)絡游戲、企業(yè)應用、電信軟件等眾多行業(yè)已經(jīng)得到了成功商用,證明它已經(jīng)完全能夠滿足不同行業(yè)的商業(yè)應用了。
正是因為這些優(yōu)點,Netty 逐漸成為了 Java NIO 編程的首選框架。
通過以上內(nèi)容幫助大家回憶一下Linux網(wǎng)絡編程中的IO模型,搞清楚同步異步阻塞非阻塞的概念,了解了BIO到NIO的區(qū)別和聯(lián)系。最后我們知道了眾多的NIO框架中,為什么要選擇Netty。這些知識也是我們后續(xù)進行網(wǎng)絡編程和Netty開發(fā)的基礎。
以上就是有關Netty的有關內(nèi)容,希望可以對大家學習Netty有幫助,喜歡的小伙伴可以幫LZ進行轉發(fā)+關注,也會不定時更新干貨!感謝大家!