Netty中的ChannelHandler的基础知识

Posted javartisan

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Netty中的ChannelHandler的基础知识相关的知识,希望对你有一定的参考价值。

Netty ChannelHandler概览     

Netty中的ChannelHandler类似于工程对产品流水线生产中:生产线上的每一步的处理器,而生产线则就是Netty中的ChannelPipeline。Netty也正是通过ChannelHandler实现了业务与底层网络的解耦。Netty中的ChannelHandler按照输出字节流向分为In,Out,Duplex三种ChannelHandler,本文主要介绍前面两种,Netty中ChannelHandler关系图如下:

  • ChannelHander是顶级抽象,基础接口类。
  • ChannelHandlerAdapter则是ChannelHandler的一个适配器,对ChannelHandler添加了新的行为,用于判断当前Handler是否执行共享,ChannelHandler共享是指:如果一个ChannelHandler支持共享,则该ChannelHandler可以被添加到多个ChannelPipeline中。
  • ChannelInboundHandler、ChannelOutboundHandler则是分别对应进站与出站的ChannelHandler处理器。
  • ChannelInboundHandlerAdpater、ChannelOutboundHandlerAdapter分别实现了对应类型的ChannelHandler之外,还实现了ChannelHandlerAdpater接口,这样就有了判断是否是共享ChannelHandler的行为。

ChannelInboundHandler

Netty中无论对应客户端还是服务器端都有读、写操作,读操作对应的就是进站操作,进站的数据都会经过ChannelInboundHandler处理,例如解码器都是ChannelInboundHandler的子类实现;本文简单介绍一下SimpleChannelInboundHandler。

SimpleChannelInboundHandler是ChannelInboundHandlerAdpater子类实现,SimpleChannelInboundHandler是一个类型敏感的处理器,在明确上游消息类型时候可以使用该处理器,这样就避免了类型的强转;在SimpleChannelInboundHandler中新增加两个方法分别是:acceptInboundMessage与channelRead0方法,同时重写了channelRead方法,在channelRead方法中进行处理类型转换,对于下游事件处理通过重写channelRead0即可,具体源码如下:


    /**
     * Returns @code true if the given message should be handled. If @code false it will be passed to the next
     * @link ChannelInboundHandler in the @link ChannelPipeline.
     */
    public boolean acceptInboundMessage(Object msg) throws Exception 
        return matcher.match(msg);
    

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception 
        boolean release = true;
        try 
            if (acceptInboundMessage(msg)) 
                @SuppressWarnings("unchecked")
                I imsg = (I) msg;
                channelRead0(ctx, imsg);
             else 
                release = false;
                ctx.fireChannelRead(msg);
            
         finally 
            if (autoRelease && release) 
                ReferenceCountUtil.release(msg);
            
        
    

    /**
     * <strong>Please keep in mind that this method will be renamed to
     * @code messageReceived(ChannelHandlerContext, I) in 5.0.</strong>
     *
     * Is called for each message of type @link I.
     *
     * @param ctx           the @link ChannelHandlerContext which this @link SimpleChannelInboundHandler
     *                      belongs to
     * @param msg           the message to handle
     * @throws Exception    is thrown if an error occurred
     */
    protected abstract void channelRead0(ChannelHandlerContext ctx, I msg) throws Exception;

备注:ChannelInitializer也是一个入站处理器,当Channel建立时候进行一些初始化工作,例如Pipeline上面添加Handler操作。

ChannelOutboundHandler

Netty中Channel上的写操作对应着出站操作,通常流经ChannelOutboundHandler处理之后通过网络将数据发出去;ChannelOutboundHandler的子类实现一般都是编码器,对应编码操作。

MessageToByteEncoder是一个类型敏感的编码器,将指定类型的Message序列化为二进制数据;该类新增三个方法分别是:acceptOutboundMessage(用于判断类型),allocateBuffer(分配缓冲区),encode(编码逻辑供子类具体实现);而write方法中进行类型的处理,判断是否类型匹配,匹配则进行encode处理。


    /**
     * Returns @code true if the given message should be handled. If @code false it will be passed to the next
     * @link ChannelOutboundHandler in the @link ChannelPipeline.
     */
    public boolean acceptOutboundMessage(Object msg) throws Exception 
        return matcher.match(msg);
    

    @Override
    public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception 
        ByteBuf buf = null;
        try 
            if (acceptOutboundMessage(msg)) 
                @SuppressWarnings("unchecked")
                I cast = (I) msg;
                buf = allocateBuffer(ctx, cast, preferDirect);
                try 
                    encode(ctx, cast, buf);
                 finally 
                    ReferenceCountUtil.release(cast);
                

                if (buf.isReadable()) 
                    ctx.write(buf, promise);
                 else 
                    buf.release();
                    ctx.write(Unpooled.EMPTY_BUFFER, promise);
                
                buf = null;
             else 
                ctx.write(msg, promise);
            
         catch (EncoderException e) 
            throw e;
         catch (Throwable e) 
            throw new EncoderException(e);
         finally 
            if (buf != null) 
                buf.release();
            
        
    

    /**
     * Allocate a @link ByteBuf which will be used as argument of @link #encode(ChannelHandlerContext, I, ByteBuf).
     * Sub-classes may override this method to return @link ByteBuf with a perfect matching @code initialCapacity.
     */
    protected ByteBuf allocateBuffer(ChannelHandlerContext ctx, @SuppressWarnings("unused") I msg,
                               boolean preferDirect) throws Exception 
        if (preferDirect) 
            //Direct Buffer
            return ctx.alloc().ioBuffer();
         else 
            //Heap Buffer
            return ctx.alloc().heapBuffer();
        
    

    /**
     * Encode a message into a @link ByteBuf. This method will be called for each written message that can be handled
     * by this encoder.
     *
     * @param ctx           the @link ChannelHandlerContext which this @link MessageToByteEncoder belongs to
     * @param msg           the message to encode
     * @param out           the @link ByteBuf into which the encoded message will be written
     * @throws Exception    is thrown if an error occurs
     */
    protected abstract void encode(ChannelHandlerContext ctx, I msg, ByteBuf out) throws Exception;

ChannelDuplexHandler

经过如上的进站、出站ChannelHandler的理解基础之上,对于ChannelDuplexHandler理解就比较容易了,ChannelDuplexHandler是一个双工的处理器,既可以处理入站信息,也可以处理出站信息,通常对应的也就是编解码器(Codec-编解码器)。

Netty中的MessageToMessageCodec就是一个典型的编解码器,该编解码器新增加了4个方法,分别进站对应两个,出站对应两个,对于encode与decode由子类具体实现响应的编码与解码。如下是一个简单的Long与Integer类型之间编码解码的实现:

public class NumberCodec extends MessageToMessageCodec<Integer, Long> 
    @Override
    public Long decode(ChannelHandlerContext ctx, Integer msg, List<Object> out)
            throws Exception 
        out.add(msg.longValue());
    

    @Override
    public Integer encode(ChannelHandlerContext ctx, Long msg, List<Object> out)
            throws Exception 
        out.add(msg.intValue());
    

 

以上是关于Netty中的ChannelHandler的基础知识的主要内容,如果未能解决你的问题,请参考以下文章

netty源码解解析(4.0)-16 ChannelHandler概览

Netty ChannelHandler之概述

Netty实战六之ChannelHandler和ChannelPipeline

5. Netty源码分析之ChannelPipeline 和 ChannelHanler

11 预置的ChannelHandler和编解码器

95-33-020-ChannelHandler-ChannelHandler简介