dotnetty源碼解讀一些要點

DefaultAttributeMap

它綁定在Channel或者ChannelHandlerContext上的一個附件。 ChannelHandlerContext都是ChannelHandler和ChannelPipeline之間鏈接的橋樑, 每個ChannelHandlerContext都有屬於本身的上下文, 每個ChannelHandlerContext上若是有AttributeMap都是綁定上下文的, A的ChannelHandlerContext中的AttributeMap,B的ChannelHandlerContext是沒法讀取到的。 可是Channel上的AttributeMap是共享的,每個ChannelHandler都能獲取到。bootstrap

AbstractUnsafe

unsafe 是內部接口, 從設計上說這個接口的全部實現都是netty的內部代碼,只能被netty本身使用. netty的使用者時不該該 直接調用這些內部實現的.Unsafe 操做不能被用戶代碼調用.這些方法僅僅用於實現具體的transport, 並且必須被I/O線程調用.
unsafe 特別關鍵, 它封裝了對底層 Socket 的操做, 所以其實是溝通 Netty 上層和 底層的重要的橋樑.promise

  1. RegisterAsync 會把eventloop註冊進來
  2. 其內部方法.大可能是經過pipe管道的head調用的.channel.bind->pipe.bind->tail.bind->head.bind->unsafe.bind

SocketChannelAsyncOperation

繼承與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
  1. 客戶端執行流程 待作

由上可見,netty中channel的任何調用都會先通過管道.再由管道按照順序進行執行.而unsafe是socket底層的一些操做.也老是會被默認執行(或者在head中或者直接在pipe中)socket

DefaultChannelPipeline

  1. DefaultChannelPipeline 中, 還有兩個特殊的字段, 即 head 和 tail, 而這兩個字段是一個雙向鏈表的頭和尾. 其實在 DefaultChannelPipeline 中, 維護了一個以 AbstractChannelHandlerContext 爲節點的雙向鏈表, 這個鏈表是 Netty 實現 Pipeline 機制的關鍵.
  2. 即 header 是一個 outboundHandler, 而 tail 是一個inboundHandler,

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 直接的調用關係是否容易混淆, 所以讀者在閱讀這裏的源碼時, 須要特別的注意.

AbstractChannelHandlerContext

  1. FindContextInbound 同下
  2. FindContextOutbound 從 DefaultChannelPipeline 內的雙向鏈表的 tail 開始, 不斷向前尋找第一個 outbound 爲 true 的 AbstractChannelHandlerContext, 而後調用它的 invokeConnect 方法,在 DefaultChannelPipeline 的構造器中, 會實例化兩個對象: head 和 tail, 並造成了雙向鏈表的頭和尾. head 是 HeadContext 的實例, 它實現了 ChannelOutboundHandler 接口, 而且它的 outbound 字段爲 true. 所以在 findContextOutbound 中, 找到的 AbstractChannelHandlerContext 對象其實就是 head.
  3. HeadContext 和 TailContext 繼承於 AbstractChannelHandlerContext 的同時也實現了 ChannelHandler 接口了, 所以它們有 Context 和 Handler 的雙重屬性.

ChannelInitializer

此類就是一個向管道註冊處理器的適配器,在channel執行Registered的時候,向管道中注入一些處理.而後把該管道處理方法刪除

bossGroup 與 workerGroup

  1. 其實呢, bossGroup 是用於服務端 的 accept 的, 即用於處理客戶端的鏈接請求. 咱們能夠把 Netty 比做一個飯店, bossGroup 就像一個像一個前臺接待, 當客戶來到飯店吃時, 接待員就會引導顧客就坐, 爲顧客端茶送水等. 而 workerGroup, 其實就是實際上幹活的啦, 它們負責客戶端鏈接通道的 IO 操做: 當接待員 招待好顧客後, 就能夠稍作休息, 而此時後廚裏的廚師們(workerGroup)就開始忙碌地準備飯菜了.
  2. 服務器端 bossGroup 不斷地監聽是否有客戶端的鏈接, 當發現有一個新的客戶端鏈接到來時, bossGroup 就會爲此鏈接初始化各項資源, 而後從 workerGroup 中選出一個 EventLoop 綁定到此客戶端鏈接中. 那麼接下來的服務器與客戶端的交互過程就所有在此分配的 EventLoop 中了

     

  3. 服務器端的 ServerSocketChannel 只綁定到了 bossGroup 中的一個線程, 所以在調用 Java NIO 的 Selector.select 處理客戶端的鏈接請求時, 其實是在一個線程中的, 因此對只有一個服務的應用來講, bossGroup 設置多個線程是沒有什麼做用的, 反而還會形成資源浪費.:netty做者說:咱們在不一樣的服務器引導之間共享NioEventLoopGroup,多個boss線程是有用的,

  4. bossgroup中的socket是非阻塞的..由於其只是接受新的socket..若是在處理accept的時候阻塞了就會影響新的socket創建.因此不能是阻塞的.workgroup內的socket是阻塞的由於他要等待一個請求處理完成,要否則多個請求,修改程序某個變量會形成鎖的問題.

bootstrap

serverbootstrap在channel初始化的時候會.向channel中寫入各類配置包括基本的管道處理
* 服務器端的 handler 與 childHandler 的區別與聯繫:
* 在服務器 NioServerSocketChannel 的 pipeline 中添加的是 handler 與 ServerBootstrapAcceptor.
* 當有新的客戶端鏈接請求時, ServerBootstrapAcceptor.channelRead 中負責新建此鏈接的 NioSocketChannel 並添加 childHandler 到 NioSocketChannel 對應的 pipeline 中, 並將此 channel 綁定到 workerGroup 中的某個 eventLoop 中.
* handler 是在 accept 階段起做用, 它處理客戶端的鏈接請求.
* childHandler 是在客戶端鏈接創建之後起做用, 它負責客戶端鏈接的 IO 交互.

SingleThreadEventLoop

在 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 事件.

相關文章
相關標籤/搜索