1. 程式人生 > >一起學Netty(四)之 ChannelHandler,ChannelHandlerContext,ChannelPipeline

一起學Netty(四)之 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的啟動類程式碼:

try {
            Bootstrap b = new Bootstrap();
            b.group(group)
             .channel(NioSocketChannel.class)
             .option(ChannelOption.TCP_NODELAY, true)
             .handler(new ChannelInitializer<SocketChannel>() {
                 @Override
                 public void initChannel(SocketChannel ch) throws Exception {
                     ChannelPipeline p = ch.pipeline();
                     p.addLast("decoder", new StringDecoder());
                     p.addLast("encoder", new StringEncoder());
                     p.addLast(new BaseClient1Handler());
                     p.addLast(new BaseClient2Handler());
                 }
             });

            ChannelFuture future = b.connect(HOST, PORT).sync();
            future.channel().writeAndFlush("Hello Netty Server ,I am a common client");
            future.channel().closeFuture().sync();
        } finally {
            group.shutdownGracefully();
        }
我們在channelhandler鏈中加了兩個自定義的BaseClient1Handler和BaseClient2Handler的處理器

BaseClient1Handler的方法也很簡單:

BaseClient1Handler.java

package com.lyncc.netty.component.channelhandler;

import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;

/**
 * 
 * @author bazingaLyncc
 * 描述:客戶端的第一個自定義的inbound處理器
 * 時間  2016年5月3日
 */
public class BaseClient1Handler extends ChannelInboundHandlerAdapter{
    
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        System.out.println("BaseClient1Handler channelActive");
//        ctx.fireChannelActive();
    }
    
    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        System.out.println("BaseClient1Handler channelInactive");
    }

}
BaseClient2Handler.java
package com.lyncc.netty.component.channelhandler;

import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;

/**
 * 
 * @author bazingaLyncc
 * 描述:客戶端的第二個自定義的inbound處理器
 * 時間  2016年5月3日
 */
public class BaseClient2Handler extends ChannelInboundHandlerAdapter{
    
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        System.out.println("BaseClient2Handler Active");
    }
    
   

}
其他的程式碼不修改的,我們先啟動伺服器端,然後啟動客戶端,你會發現控制檯列印了:

不會列印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之間的關係就是如上總結的

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