ChannelHandler,ChannelHandlerContext,ChannelPipeline


本小節一起學習一下ChannelHandler,ChannelHandlerContext,ChannelPipeline這三個Netty常用的組件,不探究它們的底層源碼,我們就簡單的分析一下用法

 

首先先分析一下ChannelHandler,ChannelHandler是我們日常開發中使用最多的組件了,大概我們平時寫的最多的組件就是Handler了,繼承圖如下

 

我們平時繼承的最多的就是ChannelInboundHandlerAdapter和ChannelOutboundHandlerAdapter,這兩個不是接口也不是抽象類,所以我們可以僅僅重寫我們需要的方法,沒有必須要實現的方法,當然我們也會使用SimpleChannelInboundHandler,這個類我們上個小節也稍微講了它的優缺點,這里不贅述

 

ChannelHandler,ChannelHandlerContext,ChannelPipeline這三者的關系很特別,相輔相成,一個ChannelPipeline中可以有多個ChannelHandler實例,而每一個ChannelHandler實例與ChannelPipeline之間的橋梁就是ChannelHandlerContext實例,如圖所示:

看圖就知道,ChannelHandlerContext的重要性了,如果你獲取到了ChannelHandlerContext的實例的話,你可以獲取到你想要的一切,你可以根據ChannelHandlerContext執行ChannelHandler中的方法,我們舉個例子來說,我們可以看下ChannelHandlerContext部分API:

這幾個API都是使用比較頻繁的,都是調用當前handler之后同一類型的channel中的某個方法,這里的同一類型指的是同一個方向,比如inbound調用inbound,outbound調用outbound類型的channel,一般來說,都是一個channel的ChannnelActive方法中調用fireChannelActive來觸發調用下一個handler中的ChannelActive方法

 

我們舉例來說,我們修改Helloworld版中的部分代碼,在客戶端中的channel中修改一下代碼

首先我們修改一下bootstrap的啟動類代碼:

 

[html]  view plain  copy
 
  1. try {  
  2.             Bootstrap b = new Bootstrap();  
  3.             b.group(group)  
  4.              .channel(NioSocketChannel.class)  
  5.              .option(ChannelOption.TCP_NODELAY, true)  
  6.              .handler(new ChannelInitializer<SocketChannel>() {  
  7.                  @Override  
  8.                  public void initChannel(SocketChannel ch) throws Exception {  
  9.                      ChannelPipeline p = ch.pipeline();  
  10.                      p.addLast("decoder", new StringDecoder());  
  11.                      p.addLast("encoder", new StringEncoder());  
  12.                      p.addLast(new BaseClient1Handler());  
  13.                      p.addLast(new BaseClient2Handler());  
  14.                  }  
  15.              });  
  16.   
  17.             ChannelFuture future = b.connect(HOST, PORT).sync();  
  18.             future.channel().writeAndFlush("Hello Netty Server ,I am a common client");  
  19.             future.channel().closeFuture().sync();  
  20.         } finally {  
  21.             group.shutdownGracefully();  
  22.         }  

我們在channelhandler鏈中加了兩個自定義的BaseClient1Handler和BaseClient2Handler的處理器

 

BaseClient1Handler的方法也很簡單:

BaseClient1Handler.java

 

[java]  view plain  copy
 
  1. package com.lyncc.netty.component.channelhandler;  
  2.   
  3. import io.netty.channel.ChannelHandlerContext;  
  4. import io.netty.channel.ChannelInboundHandlerAdapter;  
  5.   
  6. /** 
  7.  *  
  8.  * @author bazingaLyncc 
  9.  * 描述:客戶端的第一個自定義的inbound處理器 
  10.  * 時間  2016年5月3日 
  11.  */  
  12. public class BaseClient1Handler extends ChannelInboundHandlerAdapter{  
  13.       
  14.     @Override  
  15.     public void channelActive(ChannelHandlerContext ctx) throws Exception {  
  16.         System.out.println("BaseClient1Handler channelActive");  
  17. //        ctx.fireChannelActive();  
  18.     }  
  19.       
  20.     @Override  
  21.     public void channelInactive(ChannelHandlerContext ctx) throws Exception {  
  22.         System.out.println("BaseClient1Handler channelInactive");  
  23.     }  
  24.   
  25. }  

BaseClient2Handler.java

 

 

[java]  view plain  copy
 
  1. package com.lyncc.netty.component.channelhandler;  
  2.   
  3. import io.netty.channel.ChannelHandlerContext;  
  4. import io.netty.channel.ChannelInboundHandlerAdapter;  
  5.   
  6. /** 
  7.  *  
  8.  * @author bazingaLyncc 
  9.  * 描述:客戶端的第二個自定義的inbound處理器 
  10.  * 時間  2016年5月3日 
  11.  */  
  12. public class BaseClient2Handler extends ChannelInboundHandlerAdapter{  
  13.       
  14.     @Override  
  15.     public void channelActive(ChannelHandlerContext ctx) throws Exception {  
  16.         System.out.println("BaseClient2Handler Active");  
  17.     }  
  18.       
  19.      
  20.   
  21. }  

其他的代碼不修改的,我們先啟動服務器端,然后啟動客戶端,你會發現控制台打印了:

 

不會打印BaseClient2Handler類中channelActive方法中的輸出語句,如果你想要打印,你可以將BaseClient1Handler中的channelActive的ctx.fireChannelActive()注釋去掉。重新運行:

也就是說如果一個channelPipeline中有多個channelHandler時,且這些channelHandler中有同樣的方法時,例如這里的channelActive方法,只會調用處在第一個的channelHandler中的channelActive方法,如果你想要調用后續的channelHandler的同名的方法就需要調用以“fire”為開頭的方法了,這樣做很靈活

 

目前來說這樣做的好處:

1)每一個handler只需要關注自己要處理的方法,如果你不關注channelActive方法時,你自定義的channelhandler就不需要重寫channelActive方法

2)異常處理,如果 exceptionCaught方法每個handler都重寫了,只需有一個類捕捉到然后做處理就可以了,不需要每個handler都處理一遍

3)靈活性。例如如下圖所示:

如圖所示在業務邏輯處理中,也許左側第一個ChannelHandler根本不需要管理某個業務邏輯,但是從第二個ChannelHandler就需要關注處理某個業務需求了,那么就可以很靈活地從第二個ChannelHandler開始處理業務,不需要從channel中的第一個ChannelHandler開始處理,這樣會使代碼顯得讓人看不懂~

 

初步看懂的ChannelHandler,ChannelHandlerContext,ChannelPipeline之間的關系就是如上總結的

 

以上三點是我自己總結的,沒看源碼,有些也可能不對,歡迎拍磚,一起學習的過程,不保證全部對~


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM