一、概述
1.1、原生NIO存在的問題
1)NIO的類庫和API複雜,使用麻煩,需要熟練掌握Selector,ServerSocketChannel、SocketChannel、ByteBuffer等。
2)需要具備其他的額外技能,需要熟悉Java多線程編程,因爲NIO涉及到Reactor模式,必須熟悉多線程和網絡編程,才能寫出高質量的NIO程序
3)開發工作量和難度比較大,如,客戶端面臨斷連重連、網絡閃斷,半包讀寫、失敗緩存、網絡擁塞和異常流的處理等
4)JDK NIO的bug,如Epoll Bug,他會導致Selector空輪詢,最終導致CPU100%,直到jdk1.7版本該問題仍存在,沒有根本解決。
1.2、Netty介紹
1.2.1、官網介紹
官網地址:https://netty.io/
官網含義:Netty is an asynchronous event-driven network application framework for rapid development of maintainable high performance protocol servers & clients.
1)開源框架,異步的、基於事件驅動的網絡應用程序框架,用以快速開發高性能、高可靠的網絡IO程序。
2)簡化現有NIO開發流程
3)使用廣泛,如大數據、遊戲、通信,如ElasticSearch、Dubbo等。
1.2.2、優點
1)對JDK自帶的NIO進行了封裝,解決上述bug
2)設計優雅:適用於各種傳輸類型統一API阻塞和非阻塞Socket;基於靈活且可擴展的事件模型,可以清晰地分離關注點;高度可定製的線程模型-單線程,一個或多個線程池。
3)適用方便,無其他依賴項,JDK5(3.x)或6(netty 4.x)
4)高性能、吞吐量更高;延遲更低、減少資源消耗;最小化不必要的內存複製
5)安全;完整的ssl\TLS和StartTLS支持
1.2.3、版本
netty3.X 比較舊;netty5.x廢棄;netty4.x推薦適用,適用maven引入
<dependency> <groupId>io.netty</groupId> <artifactId>netty-all</artifactId> <version>4.1.65.Final</version> </dependency>
二、Netty線程模型
2.1、 線程模型介紹
不同線程模型,對程序的性能影響很大,目前從存在的線程模型有:傳統阻塞IO模型和Reactor模式;
Reactor模式
根據Reactor的數量和處理資源池線程的數量不同,有3種典型的實現:單Reactor單線程、單Reactor多線程、主從Reactor多線程
Netty線程模式:主要基於主從Reactor多線程模型做了一定改進,其中主從Reactor多線程模型有多個Reactor。
參看書籍:Scalable IO in java
2.1.1、傳統IO阻塞服務模型
1)工作原理圖
2)模型特點
a、採用阻塞IO模式獲取輸入
b、每個連接都需要獨立的線程完成數據的輸入,業務處理、數據返回
3)問題分析
a、當併發數很大,就會創建大量的線程、佔用很大系統資源
b、連接創建後,如果當前線程這哪是沒有數據可讀,該線程就會阻塞在read操作,造成線程資源浪費;
2.1.2、Reactor模式
Reactor常用叫法:反應器模式;2分發者模式(DIspatcher)3通知這模式(notifier)
a、針對傳統IO服務模型的2個缺點,解決方案:
1)基於IO複用模型,多個連接共用一個阻塞對象,應用程序只需要在一個阻塞對象等待,無需阻塞等待所有連接。當某個連接有新的數據可以處理時,操作系統通知應用程序,線程從阻塞狀態返回,開始進行業務處理
2)基於線程池服用線程資源,不必再爲每個連接創建線程,將連接完成後的業務處理任務分配給線程進行處理,一個線程可以處理多個連接的業務。
b、設計思想:IO複用結合線程池
說明:Reactor模式,通過一個或多個輸入同時傳遞給服務處理器的模式(基於事件驅動)
2)服務器端程序處理傳入的多個請求,並將它們同步分派到相應的處理線程,因此Reactor模式也叫Dispatcher模式
3)Reactor模式號私用IO複用監聽事件,收到事件後,分發給某個線程,這點就是網絡服務器高併發處理關鍵。
c、核心組成
1)Reactor,在一個單獨的線程中運行,負責監聽和分發事件,分發給適當的處理程序來對IO事件作出反應。
2)Handlers,處理程序執行IO事件要完成的實際事件。Reactor通過調度適當的處理程序來相應IO事件,處理程序執行非阻塞操作。
d、Reactor優點
1)響應快,不必爲單個同步時間所阻塞,雖然Reactor本身依然是同步的;2)可以最大程度的避免複雜的多線程及同步問題,並且避免了多線程/進程的切換開銷
3)擴展性號,可以方便的通過增加Reactor實例個數來充分利用CPU資源;4)複用性好,Reactor模型本身與具體事件處理邏輯無關,具有很高的複用性
2.1.2.1、單Reactor單線程【不常用,原Redis使用】
上節的NIO原生API的羣聊即是此種模式
方案說明:1)Select就是前面IO複用模型介紹的標準網絡編程API,可以實現應用程序通過一個阻塞對象監聽多路連接請求。
2)Reactor對象通過Select監控客戶端請求事件,收到事件後通過DIspatch進行分發
3)如果是建立連接請求事件,則由Acceptor通過Accept處理連接請求,然後創建一個Handler對象處理連接完成後的後續業務處理
4)如果不是建立連接事件,則Reactor會分發調用連接對應的Handler來響應
5)Handler會完成Read→業務處理→Send的完整業務流程
優缺點:優點:1)模型簡單,沒有多線程,進程通信、競爭的問題,全部都在一個線程中完成
缺點1)性能問題,只有一個線程,無法完全發揮多核CPU的性能。Handler在處理某個連接上的業務時,整個進程無法處理其他連接事件,很容易導致性能瓶頸
缺點2)可靠性問題,線程意外終止,或者進入死循環,會導致整個系統通信模塊不可用,不能接收和處理外部消息,造成節點故障。
使用場景:客戶端的數量有限,業務處理非常快速,比如Redis在業務處理的時間複雜度O(1)的情況。
2.1.2.2、單Reactor多線程
工作原理圖:
說明:1)Reactor對象通過Select監控客戶端請求事件,收到事件後,通過dispatch進行分發
2)如果建立連接請求,則由Acceptor通過accept處理連接請求,然後創建一個Handler對象處理完連接後的各種事件
3)如果不是連接請求,則由reactor分發調用連接對應的handler來處理
4)handler只負責響應事件,不做具體的業務處理,通過read讀取數據後,會分發給後面的worker線程池的某個線程處理業務
5)wocker線程池會分配獨立線程完成真正的業務,並將結果返回給handler
6)handler收到相應後,通過send將結果返回給client
b、優缺點
優點:可以充分利用多核CPU的處理能力
缺點:多線程數據共享和訪問比較複雜,reactor處理所有的事件的監聽和響應,在單線程運行,在高併發場景容易出現性能瓶頸
2.1.2.3、主從Reactor多線程
針對單Reactor多線程模型中,在單線程運行,在高併發場景容易出現性能瓶頸,可以讓Reactor在多線程中運行
工作原理圖
優缺點:優點:父線程與子線程的數據交互簡單職責明確,父線程只需要接收新鏈接,子線程完成後續的業務處理;
缺點:編程複雜度較高
應用:Nginx主從Reactor多進程模型,Memcached主從多線程模式,Netty主從多線程模式