Netty是一個高性能NIO框架,其是對Reactor模型的一個實現java
NIO模型web
Acceptor註冊Selector,監聽accept事件 當客戶端鏈接後,觸發accept事件 服務器構建對應的Channel,並在其上註冊Selector,監聽讀寫事件 當發生讀寫事件後,進行相應的讀寫處理
Reactor中的組件編程
Reactor:Reactor是IO事件的派發者。 Acceptor:Acceptor接受client鏈接,創建對應client的Handler,並向Reactor註冊此Handler。 Handler:和一個client通信的實體,按這樣的過程實現業務的處理。通常在基本的Handler基礎上還會有更進一步的層次劃分, 用來抽象諸如decode,process和encoder這些過程。好比對Web Server而言,decode一般是HTTP請求的解析, process的過程會進一步涉及到Listener和Servlet的調用。業務邏輯的處理在Reactor模式裏被分散的IO事件所打破, 因此Handler須要有適當的機制在所需的信息還不全(讀到一半)的時候保存上下文,並在下一次IO事件到來的時候(另外一半可讀了)能繼續中斷的處理。爲了簡化設計,Handler一般被設計成狀態機,按GoF的state pattern來實現。後端
對應上面的NIO代碼來看: Reactor:至關於有分發功能的Selector Acceptor:NIO中創建鏈接的那個判斷分支 Handler:消息讀寫處理等操做類緩存
Reactor單線程模型服務器
這個模型和上面的NIO流程很相似,只是將消息相關處理獨立到了Handler中去了! 雖然上面說到NIO一個線程就能夠支持全部的IO處理。可是瓶頸也是顯而易見的!咱們看一個客戶端的狀況,若是這個客戶端屢次進行請求,若是在Handler中的處理速度較慢,那麼後續的客戶端請求都會被積壓,致使響應變慢!因此引入了Reactor多線程模型!網絡
Reactor多線程模型多線程
Reactor多線程模型就是將Handler中的IO操做和非IO操做分開,操做IO的線程稱爲IO線程,非IO操做的線程稱爲工做線程!這樣的話,客戶端的請求會直接被丟到線程池中,客戶端發送請求就不會堵塞! 可是當用戶進一步增長的時候,Reactor會出現瓶頸!由於Reactor既要處理IO操做請求,又要響應鏈接請求!爲了分擔Reactor的負擔,因此引入了主從Reactor模型!併發
主從Reactor模型框架
主Reactor用於響應鏈接請求,從Reactor用於處理IO操做請求
幾種IO模型總結 BIO:同步阻塞式通訊 僞異步I/O:爲了解決同步阻塞 I/O 面臨的一個鏈路須要一個線程處理的問題,後來有人 對它的線程模型進行了優化,後端經過一個線程池來處理多個客戶端的請求接入, 造成客戶端個數 M:線程池最大線程數 N 的比例關係,其中 M 能夠遠遠大於 N, 經過線程池能夠靈活的調配線程資源,設置線程的最大值,防止因爲海量併發接 入致使線程耗盡。 若是通訊對方返回應答時間過長,會引發的級聯故障
NIO:它的官方叫法叫NewI/O。可是,因爲以前老的 I/O 類庫是阻塞 I/O,New I/O 類 庫的目標就是要讓 Java 支持非阻塞 I/O,因此,更多的人喜歡稱之爲非阻塞 I/ O(Non-block I/O) AIO:NIO2.0 引入了新的異步通道的概念,並提供了異步文件通道和異步套接字 通道的實現。 異步通道提供兩種方式獲取獲取操做結果: • 經過java.util.concurrent.Future類來表示異步操做的結果; • 在執行異步操做的時候傳入一個java.nio.channels; CompletionHandler接口的實現類做爲操做完成的回調。 NIO2.0(AIO) 的異步套接字通道是真正的異步非阻塞 I/O,它對應UNIX網絡編程 中的事件驅動 I/O(AIO asynchronous I/O),它不須要經過多路複用器(Selector)對註冊的通 道進行輪詢操做便可實現異步讀寫,從而簡化了 NIO 的編程模型。
epoll能夠理解爲event poll,不一樣於忙輪詢和無差異輪詢,epoll之會把哪一個流發生了怎樣的I/O事件通知咱們。此時咱們對這些流的操做都是有意義的。(複雜度下降到了O(1)) epoll提供了三個函數,epollcreate,epollctl和epollwait,epollcreate是建立一個epoll句柄;epollctl是註冊要監聽的事件類型;epollwait則是等待事件的產生
elect,poll,epoll都是IO多路複用的機制。I/O多路複用就經過一種機制,能夠監視多個描述符,一旦某個描述符就緒(通常是讀就緒或者寫就緒),可以通知程序進行相應的讀寫操做。 但select,poll,epoll本質上都是同步I/O,由於他們都須要在讀寫事件就緒後本身負責進行讀寫,也就是說這個讀寫過程是阻塞的,而異步I/O則無需本身負責進行讀寫,異步I/O的實現會負責把數據從內核拷貝到用戶空間。
使用netty而不用原生NIO緣由: 1,NIO的類庫和API繁雜,使用麻煩 2,須要具有其餘的額外技能作鋪墊,例如熟悉Java多線程編程。這是由於 NIO編程涉及到Reactor模式,你必須對多線程和網路編程很是熟悉,才能 編寫出高質量的NIO程序。 3,可靠性能力補齊,工做量和難度都很是大。例如客戶端面臨斷連重連、網 絡閃斷、半包讀寫、失敗緩存、網絡擁塞和異常碼流的處理等問題,NIO 編程的特色是功能開發相對容易,可是可靠性能力補齊的工做量和難度都 很是大。 4,JDK NIO的BUG,例如臭名昭著的epoll bug,它會致使Selector空輪詢, 最終致使CPU 100%。官方聲稱在JDK1.6版本的update18修復了該問題,但 是直到JDK1.7版本該問題仍舊存在,只不過該BUG發生機率下降了一些而 已,它並無被根本解決。
不依賴於web容器的http服務
首先設想一下咱們目前的通訊方式,使用netty mina等異步事件驅動的通訊框架,將Channel中信息都分發到Handler中去處理了,Handler中的send方法只負責不斷的發送消息,receive方法只負責不斷接收消息,這時候就產生一個問題: 客戶端如何對應同一個Channel的接收的消息和發送的消息之間的匹配呢? 這也很簡單,就須要在發送消息的時候,必需要產生一個請求id,將調用的信息連同id一塊兒發給服務器端,服務器端處理完畢後,再將響應信息和上述請求id一塊兒發給客戶端,這樣的話客戶端在接收到響應以後就能夠根據id來判斷是針對哪次請求的響應結果了。