ReplayingDecoder源码详解及应用

ReplayingDecoder介绍

ReplayingDecoder是ByteToMessageDecoder的一个特殊变体,ReplayingDecoder继承了ByteToMessageDecoder。ReplayingDecoder和ByteToMessageDecoder最大的不同在于,ReplayingDecoder允许让你实现decode()方法,就像已经接收到所有所需的字节,而不用去检查所需字节的可用性。
在自定义协议传递ByteBuf格式
ReplayingDecoder源码详解及应用_第1张图片
head通常是4个字节长度,读取出来的int数字表示要在body读的字节长度。
下面看一下ReplayingDecoder和ByteToMessageDecoder具体的区别。

ByteToMessageDecoder的实现

 public class IntegerHeaderFrameDecoder extends  ByteToMessageDecoder {
 
     @Override
    protected void decode( ChannelHandlerContext ctx,
                             ByteBuf buf, List<Object> out) throws Exception {
 
      if (buf.readableBytes() < 4) {
         return;
      }
 
      buf.markReaderIndex();
      int length = buf.readInt();
 
      if (buf.readableBytes() < length) {
         buf.resetReaderIndex();
         return;
      }
 
      out.add(buf.readBytes(length));
    }
  }

int length = buf.readInt();将readerindex移动到body的起始位置。
###ReplayingDecoder的实现

 public class IntegerHeaderFrameDecoder
       extends  ReplayingDecoder<Void> {
 
    protected void decode(ChannelHandlerContext ctx,
                             ByteBuf buf) throws Exception {
      out.add(buf.readBytes(buf.readInt()));
    }
  }

ReplayingDecoder是怎么做到的呢?
ReplayingDecoder传递一个专门的ByteBuf实现,当缓冲区中没有足够的数据时,会抛出某种类型的一个Error。在上面的IntegerHeaderFrameDecoder,当调用buf.readInt()时,只能假定缓冲区有4个或更多的字节。如果,缓冲区中确实有4个字节,会返回你所期望的integer header。否则,会抛出这个Error。如果ReplayingDecoder捕获了这个Error,会重置缓冲区的readerindex到缓冲区的开始位置,并且当更多的数据到达缓冲区时,会再次调用decode()方法。
需要注意一些限制:如果网络缓慢而且消息格式复杂,性能会变差,在这种情况,你的解码器不得不解码同一部分的信息。解码一条信息,decode()方法可以被多次调用。如下代码不能正常工作:

 public class MyDecoder extends  ReplayingDecoder<Void> {

    private final Queue<Integer> values = new LinkedList<Integer>();
    @Override
    public void decode(..,  ByteBuf buf, List<Object> out) throws Exception {
 
      // A message contains 2 integers.
      values.offer(buf.readInt());
      values.offer(buf.readInt());
 
      // This assertion will fail intermittently since values.offer()
      // can be called more than two times!
      assert values.size() == 2;
      out.add(values.poll() + values.poll());
    }
  }

正确的做法如下:

 public class MyDecoder extends  ReplayingDecoder<Void> {
 
    private final Queue<Integer> values = new LinkedList<Integer>();
    @Override
    public void decode(..,  ByteBuf buf, List<Object> out) throws Exception {
 
      // Revert the state of the variable that might have been changed
      // since the last partial decode.
      values.clear();
 
      // A message contains 2 integers.
      values.offer(buf.readInt());
      values.offer(buf.readInt());
 
      // Now we know this assertion will never fail.
      assert values.size() == 2;
      out.add(values.poll() + values.poll());
    }
  }

checkpoint()

复杂解码器的性能因为checkpoint()方法显著的提高。checkpoint()方法更新缓冲区的“初始”位置,ReplayingDecoder将回滚缓冲区的readerIndex到最后调用checkpoint()方法的位置。
管理解码器状态的最简单方法是创建表示解码器当前状态的Enum类型,并在状态改变时调用checkpoint(T)方法。你可以有许多你想要的状态取决于你想解码的信息的复杂性:


  public enum MyDecoderState {
    READ_LENGTH,
    READ_CONTENT;
  }
 
  public class IntegerHeaderFrameDecoder
       extends ReplayingDecoder<MyDecoderState> {
 
    private int length;
 
    public IntegerHeaderFrameDecoder() {
      // Set the initial state.
      super(MyDecoderState.READ_LENGTH);
    }
 
    @Override
    protected void decode( ChannelHandlerContext ctx,
                             ByteBuf buf, List<Object> out) throws Exception {
      switch (state()) {
      case READ_LENGTH:
        length = buf.readInt();
        checkpoint(MyDecoderState.READ_CONTENT);
      case READ_CONTENT:
        ByteBuf frame = buf.readBytes(length);
       checkpoint(MyDecoderState.READ_LENGTH);
        out.add(frame);
        break;
      default:
        throw new Error("Shouldn't reach here.");
      }
    }
  }

调用无参的checkpoint()

 public class IntegerHeaderFrameDecoder
       extends ReplayingDecoder<Void> {
 
    private boolean readLength
    private int length;

    Override
    protected void decode( ChannelHandlerContext ctx,
                             ByteBuf buf, List<Object> out) throws Exception {
      if (!readLength) {
        length = buf.readInt();
        readLength = true;
        checkpoint();
      }
 
      if (readLength) {
        ByteBuf frame = buf.readBytes(length);
        readLength = false;
        checkpoint();
        out.add(frame);
      }
    }
  }

你可能感兴趣的:(netty)