上下文對象使得當前channelhandler能夠與其所屬的channelpipeline以及其餘handler進行交互,能夠通知所屬channelpipeline中的下一個handler,也可動態修改其所屬的channelpipeline,具體功能以下:html
通知。經過調用channelhandlercontext提供的方法能夠調用同一個channelpipeline中的相鄰的下一個channelhandler,詳情能夠參照channelpipeline來了解事件使如何流動的。java
修改pipeline 經過調用context的pipeline()
方法能夠獲得當前handler所屬的channelpipeline。能夠在程序運行時動態增長、刪除、替換channelpipeline中的handler。數據庫
保持供後面使用 你能夠將context對象存儲在handler中,共後面使用。好比在handler提供的方法以外觸發一個事件,甚至在不一樣的線程中均可以。實例代碼以下:api
public class MyHandler extends ChannelHandlerAdapter { private ChannelHandlerContext ctx;//成員變量 public void beforeAdd(ChannelHandlerContext ctx) { this.ctx = ctx;//此處保持該ctx } public void login(String username, password) {//在自定義方法中觸發事件 ctx.write(new LoginMessage(username, password)); } ... }
存儲狀態信息 AttributeMap.attr(AttributeKey)
方法容許你存儲和訪問與handler和context相關的狀態信息。promise
一個handler能夠有多個context 因爲同一個handler實例能夠被添加到多個pipeline中,這也就意味着一個handler能夠有一個或多個context對象,所以同一個handler實例若是被添加到一個或多個pipeline一次或者屢次,當其handle方法被調用的時候,傳進來的參數context是不一樣的。一個handler被添加到pipeline多少次,他就有多少各context對象,不是見到同一個pipeline仍是不一樣的pipeline,示例代碼以下:安全
@Sharable public class FactorialHandler extends ChannelHandlerAdapter { private final AttributeKey<Integer> counter = AttributeKey.valueOf("counter"); // This handler will receive a sequence of increasing integers starting // from 1. @Override public void channelRead(ChannelHandlerContext ctx, Object msg) { Attribute<Integer> attr = ctx.getAttr(counter); Integer a = ctx.getAttr(counter).get(); if (a == null) { a = 1; } attr.set(a * (Integer) msg); } } // Different context objects are given to "f1", "f2", "f3", and "f4" even if // they refer to the same handler instance. Because the FactorialHandler // stores its state in a context object (using an AttributeKey), the factorial is // calculated correctly 4 times once the two pipelines (p1 and p2) are active. FactorialHandler fh = new FactorialHandler(); ChannelPipeline p1 = Channels.pipeline(); p1.addLast("f1", fh);//一個context p1.addLast("f2", fh); //二個context ChannelPipeline p2 = Channels.pipeline(); p2.addLast("f3", fh);//三個context p2.addLast("f4", fh);//四個context
此接口調用ChannelHandler
的事件處理方法,若是此接口的默認實現類不知足要求,用戶能夠指定一個ChannelHandlerInvoker實現一個自定義線程模型,注意這個接口的方法不是給用戶調用的。async
表示一個channel全局惟一的標識符。標識符是根據下列資源產生的:MAC地址,當前進程ID,System.currentTimeMillis()
,System.nanoTime()
,一個隨機的32-bit Integer和一個順序增加的32-bit整數。標識符的全局惟一性有賴於MAC地址和進程ID,系統會在類加載的時候自動檢測獲取,若是這兩個獲取失敗,系統會記錄警告信息,同時將使用隨機數代替做爲標識符,另外,你能夠經過系統屬性人工指定這兩個參數:io.netty.machineId
48bit或者64bit十六進制表示的整數,能夠經過冒號後者連字符分割,io.netty.processId
0到65535之間的一個整數。
ide
channelpipeline由一系列channelhandler組成,用來處理channel的inbound events 和 outbound operations。channelpipeline實現了攔截過濾器模式(Intercepting Filter pattern)的一種高級形式。以使用戶可以徹底控制一個時間(event)怎樣被處理,以及channelpipeline中的channelhandler之間如何交互。oop
pipeline的建立 每個channel都一個本身的pipeline,channel建立的時候pipeline自動建立ui
事件(event)如何在pipeline中流動 下圖展現了一個IO事件被channelpipeline中的channelhandler處理的典型方式,一個IO事件被當前handler處理以後,將被轉發到當前handler在channelpipeline中的緊鄰的下一個handler處理。若是有必要的話,channelhandler自己也能夠觸發任意的IO事件。爲了觸發或者轉發IO事件,channelhandler須要調用channelhandlercontext提供的事件傳播方法,例如ChannelHandlerContext.fireChannelRead(Object)
和 ChannelHandlerContext.write(Object)
.
I/O Request via Channel or ChannelHandlerContext | +---------------------------------------------------+---------------+ | ChannelPipeline | | | \|/ | | +----------------------------------------------+----------+ | | | ChannelHandler N | | | +----------+-----------------------------------+----------+ | | /|\ | | | | \|/ | | +----------+-----------------------------------+----------+ | | | ChannelHandler N-1 | | | +----------+-----------------------------------+----------+ | | /|\ . | | . . | | ChannelHandlerContext.fireIN_EVT() ChannelHandlerContext.OUT_EVT()| | [method call] [method call] | | . . | | . \|/ | | +----------+-----------------------------------+----------+ | | | ChannelHandler 2 | | | +----------+-----------------------------------+----------+ | | /|\ | | | | \|/ | | +----------+-----------------------------------+----------+ | | | ChannelHandler 1 | | | +----------+-----------------------------------+----------+ | | /|\ | | +---------------+-----------------------------------+---------------+ | \|/ +---------------+-----------------------------------+---------------+ | | | | | [ Socket.read() ] [ Socket.write() ] | | | | Netty Internal I/O Threads (Transport Implementation) | +-------------------------------------------------------------------+
如上圖左邊所示,一個inbound事件自底向上的被channelhandler處理,一個inbound事件一般是由IO線程觸發(圖底部),以便在channel狀態發生變化(例如新建連接或者關閉鏈接)或者從遠端讀取數據的時候通知channelhandler。若是inbound事件超過了圖中最頂端的channelhandler,該事件會被丟棄,並被記錄,取決於日誌級別。
如上圖右邊所示,一個outbound事件自頂向下的被channelhandler處理,一個outbound事件一般是由用戶的代碼調用IO操做觸發,例如寫請求或者嘗試進行連接。若是一個outbound事件超過了最底端的channelhandler,改事件會被與channel關聯的IO線程處理,IO線程一般狀況下執行實際的輸出操做,好比SocketChannel.write(ByteBuffer)
.
轉發事件給下一個handler 就像上面解釋的那樣,channelhandler經過調用channelhandlercontext的事件傳播方法將事件轉發給下一個channelhandler,具體方式以下:
Inbound event propagation methods:
Outbound event propagation methods:
下面的代碼展現了事件傳播的經常使用作法:
public class MyInboundHandler extends ChannelHandlerAdapter { @Override public void channelActive(ChannelHandlerContext ctx) { System.out.println("Connected!"); ctx.fireChannelActive();//傳播事件 } } public clas MyOutboundHandler extends ChannelHandlerAdapter { @Override public void close(ChannelHandlerContext ctx, ChannelPromise promise) { System.out.println("Closing .."); ctx.close(promise);//傳播事件 } }
構建pipeline 用戶在pipeline中應該有一個或者多個channelhandler來接收IO事件(例如 read)或者請求IO操做(例如write close)。例如,一個典型的server在每個channelpipeline中一般有以下channelhandler,可是具體細節可能不同,這取決於你的協議和業務邏輯的特性和複雜度:
Protocol Decoder - translates binary data (e.g. ByteBuf
) into a Java object.解碼,將二進制數據轉換成java對象
Protocol Encoder - translates a Java object into binary data. 編碼,講java對象轉換成二進制數據
Business Logic Handler - performs the actual business logic (e.g. database access).業務邏輯處理(例如數據庫存儲)
組裝pipeline的示例代碼以下:
static final EventExecutorGroup group = new DefaultEventExecutorGroup(16); ... ChannelPipeline pipeline = ch.pipeline(); pipeline.addLast("decoder", new MyProtocolDecoder()); pipeline.addLast("encoder", new MyProtocolEncoder()); // Tell the pipeline to run MyBusinessLogicHandler's event handler methods // in a different thread than an I/O thread so that the I/O thread is not blocked by // a time-consuming task. // If your business logic is fully asynchronous or finished very quickly, you don't // need to specify a group.若是業務邏輯是一個耗時任務或者會阻塞,請不要放在IO線程執行。 pipeline.addLast(group, "handler", new MyBusinessLogicHandler());
線程安全 channelpipeline中的channelhandler能夠在任什麼時候候添加刪除,由於channelpipeline是線程安全的。例如你能夠在發送敏感信息的時候添加一個加密的handler,在敏感信息發送以後刪除這個handler。
此接口表示一個特殊的channelfuture對象,用來展現FileRegion
transfer 的進度
An EventListener listener which will be called once the sending task associated with future is being transferred.
Special ChannelPromise
which will be notified once the associated bytes is transferring.
Special ChannelFuture
which is writable.
一旦channel註冊以後,該接口處理全部關於該channel的IO操做,一個EventLoop一般狀況下處理一個或多個channel,可是這取決於內部實現細節
他容許註冊channel,並在後面eventloop中處理channel
表示經過channel發送的一個文件區塊,能夠支持zero-copy file transfer.
此接口負責計消息的大小,也就是消息在內存中佔多大空間。
負責建立一個接收緩衝區,其大小適中,可以存儲inbound data 又不浪費空間
負責接接入的連接請求,並建立一個子channel來接收連接,具體實現能夠參照ServerSocketChannel