W3Cschool
恭喜您成為首批注冊用戶
獲得88經(jīng)驗值獎勵
在ChannelHandler 添加到 ChannelPipeline 時會創(chuàng)建一個實例,就是接口 ChannelHandlerContext,它代表了 ChannelHandler 和ChannelPipeline 之間的關(guān)聯(lián)。接口ChannelHandlerContext 主要是對通過同一個 ChannelPipeline 關(guān)聯(lián)的 ChannelHandler 之間的交互進行管理
ChannelHandlerContext 中包含了有許多方法,其中一些方法也出現(xiàn)在 Channel 和ChannelPipeline 本身。如果您通過Channel 或ChannelPipeline 的實例來調(diào)用這些方法,他們就會在整個 pipeline中傳播 。相比之下,一樣的方法在 ChannelHandlerContext 的實例上調(diào)用, 就只會從當(dāng)前的 ChannelHandler 開始并傳播到相關(guān)管道中的下一個有處理事件能力的 ChannelHandler 。
ChannelHandlerContext API 總結(jié)如下:
Table 6.10 ChannelHandlerContext API
名稱 | 描述 |
---|---|
bind | Request to bind to the given SocketAddress and return a ChannelFuture. |
channel | Return the Channel which is bound to this instance. |
close | Request to close the Channel and return a ChannelFuture. |
connect | Request to connect to the given SocketAddress and return a ChannelFuture. |
deregister | Request to deregister from the previously assigned EventExecutor and return a ChannelFuture. |
disconnect | Request to disconnect from the remote peer and return a ChannelFuture. |
executor | Return the EventExecutor that dispatches events. |
fireChannelActive | A Channel is active (connected). |
fireChannelInactive | A Channel is inactive (closed). |
fireChannelRead | A Channel received a message. |
fireChannelReadComplete | Triggers a channelWritabilityChanged event to the next |
ChannelInboundHandler. handler | Returns the ChannelHandler bound to this instance. isRemoved | Returns true if the associated ChannelHandler was removed from the ChannelPipeline. name | Returns the unique name of this instance. pipeline | Returns the associated ChannelPipeline. read | Request to read data from the Channel into the first inbound buffer. Triggers a channelRead event if successful and notifies the handler of channelReadComplete. write | Request to write a message via this instance through the pipeline.
其他注意注意事項:
本節(jié),我們將說明 ChannelHandlerContext的用法 ,以及ChannelHandlerContext, Channel 和 ChannelPipeline 這些類中方法的不同表現(xiàn)。下圖展示了 ChannelPipeline, Channel, ChannelHandler 和 ChannelHandlerContext 的關(guān)系
Figure 6.3 Channel, ChannelPipeline, ChannelHandler and ChannelHandlerContext
下面展示了, 從 ChannelHandlerContext 獲取到 Channel 的引用,通過調(diào)用 Channel 上的 write() 方法來觸發(fā)一個 寫事件到通過管道的的流中
Listing 6.6 Accessing the Channel from a ChannelHandlerContext
ChannelHandlerContext ctx = context;
Channel channel = ctx.channel(); //1
channel.write(Unpooled.copiedBuffer("Netty in Action",
CharsetUtil.UTF_8)); //2
下面展示了 從 ChannelHandlerContext 獲取到 ChannelPipeline 的相同示例
Listing 6.7 Accessing the ChannelPipeline from a ChannelHandlerContext
ChannelHandlerContext ctx = context;
ChannelPipeline pipeline = ctx.pipeline(); //1
pipeline.write(Unpooled.copiedBuffer("Netty in Action", CharsetUtil.UTF_8)); //2
流在兩個清單6.6和6.7是一樣的,如圖6.4所示。重要的是要注意,雖然在 Channel 或者 ChannelPipeline 上調(diào)用write() 都會把事件在整個管道傳播,但是在 ChannelHandler 級別上,從一個處理程序轉(zhuǎn)到下一個卻要通過在 ChannelHandlerContext 調(diào)用方法實現(xiàn)。
Figure 6.4 Event propagation via the Channel or the ChannelPipeline
為什么你可能會想從 ChannelPipeline 一個特定的點開始傳播一個事件?
想要實現(xiàn)從一個特定的 ChannelHandler 開始處理,你必須引用與 此ChannelHandler的前一個ChannelHandler 關(guān)聯(lián)的 ChannelHandlerContext 。這個ChannelHandlerContext 將會調(diào)用與自身關(guān)聯(lián)的 ChannelHandler 的下一個ChannelHandler 。
下面展示了使用場景
Listing 6.8 Events via ChannelPipeline
ChannelHandlerContext ctx = context;
ctx.write(Unpooled.copiedBuffer("Netty in Action", CharsetUtil.UTF_8));
如下所示,消息將會從下一個ChannelHandler開始流過 ChannelPipeline ,繞過所有在它之前的ChannelHandler。
Figure 6.5 Event flow for operations triggered via the ChannelHandlerContext
我們剛剛描述的用例是一種常見的情形,當(dāng)我們想要調(diào)用某個特定的 ChannelHandler操作時,它尤其有用。
正如我們在清單6.6中看到的,通過調(diào)用ChannelHandlerContext的 pipeline() 方法,你可以得到一個封閉的 ChannelPipeline 引用。這使得可以在運行時操作 pipeline 的 ChannelHandler ,這一點可以被利用來實現(xiàn)一些復(fù)雜的需求,例如,添加一個 ChannelHandler 到 pipeline 來支持動態(tài)協(xié)議改變。
其他高級用例可以實現(xiàn)通過保持一個 ChannelHandlerContext 引用供以后使用,這可能發(fā)生在任何 ChannelHandler 方法,甚至來自不同的線程。清單6.9顯示了此模式被用來觸發(fā)一個事件。
Listing 6.9 ChannelHandlerContext usage
public class WriteHandler extends ChannelHandlerAdapter {
private ChannelHandlerContext ctx;
@Override
public void handlerAdded(ChannelHandlerContext ctx) {
this.ctx = ctx; //1
}
public void send(String msg) {
ctx.writeAndFlush(msg); //2
}
}
因為 ChannelHandler 可以屬于多個 ChannelPipeline ,它可以綁定多個 ChannelHandlerContext 實例。然而,ChannelHandler 用于這種用法必須添加 @Sharable
注解。否則,試圖將它添加到多個 ChannelPipeline 將引發(fā)一個異常。此外,它必須既是線程安全的又能安全地使用多個同時的通道(比如,連接)。
清單6.10顯示了此模式的正確實現(xiàn)。
Listing 6.10 A shareable ChannelHandler
@ChannelHandler.Sharable //1
public class SharableHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
System.out.println("channel read message " + msg);
ctx.fireChannelRead(msg); //2
}
}
上面這個 ChannelHandler 實現(xiàn)符合所有包含在多個管道的要求;它通過@Sharable
注解,并不持有任何狀態(tài)。而下面清單6.11中列出的情況則恰恰相反,它會造成問題。
Listing 6.11 Invalid usage of @Sharable
@ChannelHandler.Sharable //1
public class NotSharableHandler extends ChannelInboundHandlerAdapter {
private int count;
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
count++; //2
System.out.println("inboundBufferUpdated(...) called the "
+ count + " time"); //3
ctx.fireChannelRead(msg);
}
}
這段代碼的問題是它持有狀態(tài):一個實例變量保持了方法調(diào)用的計數(shù)。將這個類的一個實例添加到 ChannelPipeline 并發(fā)訪問通道時很可能產(chǎn)生錯誤。(當(dāng)然,這個簡單的例子中可以通過在 channelRead() 上添加 synchronized 來糾正 )
總之,使用@Sharable
的話,要確定 ChannelHandler 是線程安全的。
為什么共享 ChannelHandler
常見原因是要在多個 ChannelPipelines 上安裝一個 ChannelHandler 以此來實現(xiàn)跨多個渠道收集統(tǒng)計數(shù)據(jù)的目的。
我們的討論 ChannelHandlerContext 及與其他框架組件關(guān)系的 到此結(jié)束。接下來我們將解析 Channel 狀態(tài)模型,準(zhǔn)備仔細(xì)看看ChannelHandler 本身。
Copyright©2021 w3cschool編程獅|閩ICP備15016281號-3|閩公網(wǎng)安備35020302033924號
違法和不良信息舉報電話:173-0602-2364|舉報郵箱:jubao@eeedong.com
掃描二維碼
下載編程獅App
編程獅公眾號
聯(lián)系方式:
更多建議: