它綁定在Channel或者ChannelHandlerContext上的一個附件。 ChannelHandlerContext都是ChannelHandler和ChannelPipeline之間鏈接的橋樑, 每個ChannelHandlerContext都有屬於本身的上下文, 每個ChannelHandlerContext上若是有AttributeMap都是綁定上下文的, A的ChannelHandlerContext中的AttributeMap,B的ChannelHandlerContext是沒法讀取到的。 可是Channel上的AttributeMap是共享的,每個ChannelHandler都能獲取到。bootstrap
unsafe 是內部接口, 從設計上說這個接口的全部實現都是netty的內部代碼,只能被netty本身使用. netty的使用者時不該該 直接調用這些內部實現的.Unsafe 操做不能被用戶代碼調用.這些方法僅僅用於實現具體的transport, 並且必須被I/O線程調用.
unsafe 特別關鍵, 它封裝了對底層 Socket 的操做, 所以其實是溝通 Netty 上層和 底層的重要的橋樑.promise
繼承與SocketAsyncEventArgs,當出發完成事件的時候會執行AbstractSocketChannel.IoCompletedCallback
1. 服務器端執行流程
* bootstrap.BindAsync()->channel.BindAsync->pipeline.BindAsync->tail.BindAsync->head.BindAsync->unsafe.BindAsync
* channel.DoBind() 使用socket綁定並監聽.並將channel設置爲active
* this.channel.pipeline.FireChannelActive() 出發管道中的信號激活
* channel.read->pipe.read->unsafe.BeginRead
* channel.DoBeginRead->channel.ScheduleSocketRead
* 使用ReadOperation(SocketChannelAsyncOperation
實例)進行receiveasync
* unsafe.FinishRead 實例化TcpSocketChannel保存新創建的socket並觸發pipe.FireChannelRead
* bootstrap的時候會添加ServerBootstrapAcceptor.這個時候會觸發
* 將bootstrap中的child相關的屬性設置給新的channel
* 在新的channel實例化的時候仍然會實例化SocketAsyncEventArgs而後就通過receivea完成事件
* AbstractSocketByteChannel.FinishRead 是完成讀取字節的關鍵
TcpServerSocketChannelUnsafe.FinishRead服務器
``` while (allocHandle.ContinueReading()) { connectedSocket = ch.Socket.Accept(); message = this.PrepareChannel(connectedSocket); connectedSocket = null; ch.ReadPending = false; pipeline.FireChannelRead(message); allocHandle.IncMessagesRead(1); } ``` 這一段不知道幹什麼的..由於socket是非阻塞的.在調用第一句 ch.Socket.Accept();就會拋出異常..(SocketException ex) when (ex.SocketErrorCode == SocketError.WouldBlock) https://msdn.microsoft.com/zh-cn/library/system.net.sockets.socket.accept.aspx
由上可見,netty中channel的任何調用都會先通過管道.再由管道按照順序進行執行.而unsafe是socket底層的一些操做.也老是會被默認執行(或者在head中或者直接在pipe中)socket
AbstractChannelHandlerContext 中有 inbound 和 outbound 兩個 boolean 變量, 分別用於標識 Context 所對應的 handler 的類型, 即:
* inbound 爲真時, 表示對應的 ChannelHandler 實現了 ChannelInboundHandler 方法.
* outbound 爲真時, 表示對應的 ChannelHandler 實現了 ChannelOutboundHandler 方法.async
I/O Request via Channel or ChannelHandlerContext | +---------------------------------------------------+---------------+ | ChannelPipeline | | | \|/ | | +---------------------+ +-----------+----------+ | |head| Inbound Handler N | | Outbound Handler 1 |head| | +----------+----------+ +-----------+----------+ | | /|\ | | | | \|/ | | +----------+----------+ +-----------+----------+ | | | Inbound Handler N-1 | | Outbound Handler 2 | | | +----------+----------+ +-----------+----------+ | | /|\ . | | . . | | ChannelHandlerContext.fireIN_EVT() ChannelHandlerContext.OUT_EVT()| | [ method call] [method call] | | . . | | . \|/ | | +----------+----------+ +-----------+----------+ | | | Inbound Handler 2 | | Outbound Handler M-1 | | | +----------+----------+ +-----------+----------+ | | /|\ | | | | \|/ | | +----------+----------+ +-----------+----------+ | |tail| Inbound Handler 1 | | Outbound Handler M |tail| | +----------+----------+ +-----------+----------+ | | /|\ | | +---------------+-----------------------------------+---------------+ | \|/ +---------------+-----------------------------------+---------------+ | | | | | [ Socket.read() ] [ Socket.write() ] | | | | Netty Internal I/O Threads (Transport Implementation) | +-------------------------------------------------------------------+
inbound 事件和 outbound 事件的流向是不同的, inbound 事件的流行是從下至上, 而 outbound 恰好相反, 是從上到下. 而且 inbound 的傳遞方式是經過調用相應的 ChannelHandlerContext.fireIN_EVT() 方法, 而 outbound 方法的的傳遞方式是經過調用 ChannelHandlerContext.OUT_EVT() 方法. 例如 ChannelHandlerContext.fireChannelRegistered() 調用會發送一個 ChannelRegistered 的 inbound 給下一個ChannelHandlerContext,ide
Outbound 事件的傳播方向是 tail -> customContext -> head.
Inbound 的特色是它傳播方向是 head -> customContext -> tail.oop
Inbound 事件傳播方法有:this
ChannelHandlerContext.fireChannelRegistered() ChannelHandlerContext.fireChannelActive() ChannelHandlerContext.fireChannelRead(Object) ChannelHandlerContext.fireChannelReadComplete() ChannelHandlerContext.fireExceptionCaught(Throwable) ChannelHandlerContext.fireUserEventTriggered(Object) ChannelHandlerContext.fireChannelWritabilityChanged() ChannelHandlerContext.fireChannelInactive() ChannelHandlerContext.fireChannelUnregistered()
Oubound 事件傳輸方法有:.net
ChannelHandlerContext.bind(SocketAddress, ChannelPromise) ChannelHandlerContext.connect(SocketAddress, SocketAddress, ChannelPromise) ChannelHandlerContext.write(Object, ChannelPromise) ChannelHandlerContext.flush() ChannelHandlerContext.read() ChannelHandlerContext.disconnect(ChannelPromise) ChannelHandlerContext.close(ChannelPromise)
注意, 若是咱們捕獲了一個事件, 而且想讓這個事件繼續傳遞下去, 那麼須要調用 Context 相應的傳播方法.
例如:線程
public class MyInboundHandler extends ChannelInboundHandlerAdapter { @Override public void channelActive(ChannelHandlerContext ctx) { System.out.println("Connected!"); ctx.fireChannelActive(); } } public clas MyOutboundHandler extends ChannelOutboundHandlerAdapter { @Override public void close(ChannelHandlerContext ctx, ChannelPromise promise) { System.out.println("Closing .."); ctx.close(promise); } }
上面的例子中, MyInboundHandler 收到了一個 channelActive 事件, 它在處理後, 若是但願將事件繼續傳播下去, 那麼須要接着調用 ctx.fireChannelActive().
對於 Outbound事件:
* Outbound 事件是請求事件(由 Connect 發起一個請求, 並最終由 unsafe 處理這個請求)
* Outbound 事件的發起者是 Channel
* Outbound 事件的處理者是 unsafe
* Outbound 事件在 Pipeline 中的傳輸方向是 tail -> head.
* 在 ChannelHandler 中處理事件時, 若是這個 Handler 不是最後一個 Hnalder, 則須要調用 ctx.xxx (例如 ctx.connect) 將此事件繼續傳播下去. 若是不這樣作, 那麼此事件的傳播會提早終止.
* Outbound 事件流: Context.OUT_EVT -> Connect.findContextOutbound -> nextContext.invokeOUT_EVT -> nextHandler.OUT_EVT -> nextContext.OUT_EVT
對於 Inbound 事件:
* Inbound 事件是通知事件, 當某件事情已經就緒後, 通知上層.
* Inbound 事件發起者是 unsafe
* Inbound 事件的處理者是 Channel, 若是用戶沒有實現自定義的處理方法, 那麼Inbound 事件默認的處理者是 TailContext, 而且其處理方法是空實現.
* Inbound 事件在 Pipeline 中傳輸方向是 head -> tail
* 在 ChannelHandler 中處理事件時, 若是這個 Handler 不是最後一個 Hnalder, 則須要調用 ctx.fireIN_EVT (例如 ctx.fireChannelActive) 將此事件繼續傳播下去. 若是不這樣作, 那麼此事件的傳播會提早終止.
* Outbound 事件流: Context.fireIN_EVT -> Connect.findContextInbound -> nextContext.invokeIN_EVT -> nextHandler.IN_EVT -> nextContext.fireIN_EVT
* outbound 和 inbound 事件十分的鏡像, 而且 Context 與 Handler 直接的調用關係是否容易混淆, 所以讀者在閱讀這裏的源碼時, 須要特別的注意.
此類就是一個向管道註冊處理器的適配器,在channel執行Registered的時候,向管道中注入一些處理.而後把該管道處理方法刪除
服務器端的 ServerSocketChannel 只綁定到了 bossGroup 中的一個線程, 所以在調用 Java NIO 的 Selector.select 處理客戶端的鏈接請求時, 其實是在一個線程中的, 因此對只有一個服務的應用來講, bossGroup 設置多個線程是沒有什麼做用的, 反而還會形成資源浪費.:netty做者說:咱們在不一樣的服務器引導之間共享NioEventLoopGroup,多個boss線程是有用的,
bossgroup中的socket是非阻塞的..由於其只是接受新的socket..若是在處理accept的時候阻塞了就會影響新的socket創建.因此不能是阻塞的.workgroup內的socket是阻塞的由於他要等待一個請求處理完成,要否則多個請求,修改程序某個變量會形成鎖的問題.
serverbootstrap在channel初始化的時候會.向channel中寫入各類配置包括基本的管道處理
* 服務器端的 handler 與 childHandler 的區別與聯繫:
* 在服務器 NioServerSocketChannel 的 pipeline 中添加的是 handler 與 ServerBootstrapAcceptor.
* 當有新的客戶端鏈接請求時, ServerBootstrapAcceptor.channelRead 中負責新建此鏈接的 NioSocketChannel 並添加 childHandler 到 NioSocketChannel 對應的 pipeline 中, 並將此 channel 綁定到 workerGroup 中的某個 eventLoop 中.
* handler 是在 accept 階段起做用, 它處理客戶端的鏈接請求.
* childHandler 是在客戶端鏈接創建之後起做用, 它負責客戶端鏈接的 IO 交互.
在 AbstractScheduledEventExecutor 中, Netty 實現了 NioEventLoop 的 schedule 功能, 即咱們能夠經過調用一個 NioEventLoop 實例的 schedule 方法來運行一些定時任務. 而在 SingleThreadEventLoop 中, 又實現了任務隊列的功能, 經過它, 咱們能夠調用一個 NioEventLoop 實例的 execute 方法來向任務隊列中添加一個 task, 並由 NioEventLoop 進行調度執行.
一般來講, NioEventLoop 肩負着兩種任務, 第一個是做爲 IO 線程, 執行與 Channel 相關的 IO 操做, 包括 調用 select 等待就緒的 IO 事件、讀寫數據與數據的處理等; 而第二個任務是做爲任務隊列, 執行 taskQueue 中的任務, 例如用戶調用 eventLoop.schedule 提交的定時任務也是這個線程執行的.
Netty 的 IO 處理循環netty 中必然有一個 Selector 線程, 用於不斷調用 Java NIO 的 Selector.select 方法, 查詢當前是否有就緒的 IO 事件.