当前位置: 首页 > 后端技术 > Java

netty系列:ReplayingDecoder介绍

时间:2023-04-02 10:35:31 Java

,netty中的一个自动解码器netty提供了一个从ByteBuf到用户自定义消息的解码器,叫做ByteToMessageDecoder。要使用这个解码器,我们需要继承这个解码器并实现decode方法,这样在这个方法中就实现了ByteBuf中的内容到用户自定义消息对象的转换。那么在使用ByteToMessageDecoder的过程中会遇到哪些问题呢?为什么还有另一个ReplayingDecoder?我们带着这个问题来看一下。ByteToMessageDecoder可能遇到的问题如果想实现自己的decoder将ByteBuf转换成自己的message对象,可以继承ByteToMessageDecoder,然后实现decode方法。我们先看decode方法的定义:protectedvoiddecode(ChannelHandlerContextctx,ByteBufbuf,Listout)throwsException入参中,buf为待解码的ByteBuf,out为解码后的对象列表。我们需要将ByteBuf中的数据转换成我们自己的对象,添加到out列表中。那么这里可能会出现问题,因为我们调用decode方法的时候buf中的数据可能还没有准备好,比如我们需要一个Integer,但是buf中的数据不够整型,那么buf中的一些数据逻辑是否需要判断,我们用一个带有消息长度的Buf对象来描述这个过程。所谓有消息长度的Buf对象,就是Buf消息中的前4位构成一个整数,这个整数代表buf中后续消息的长度。所以读取消息进行转换的过程就是先读取前4个字节得到消息的长度,然后再读取长度的字节,也就是我们真正想要得到的消息内容。看看如果继承自ByteToMessageDecoder,这个逻辑如何实现?publicclassIntegerHeaderFrameDecoderextendsByteToMessageDecoder{@Overrideprotectedvoiddecode(ChannelHandlerContextctx,ByteBufbuf,Listout)throwsException{if(buf.readableBytes()<4){返回;}buf.markerintlth();buf.readInt();if(buf.readableBytes()extendsByteToMessageDecoder在ByteToMessageDecoder中,最重要的方法是channelRead,其中callDecode(ctx,accumulation,out);实现从累加到输出的解码过程。ReplayingDecoder的秘诀就在于对这个方法的重写。我们看一下这个方法的具体实现:protectedvoidcallDecode(ChannelHandlerContextctx,ByteBufin,Listout){尝试{while(in.isReadable()){intoldReaderIndex=checkpoint=in.readerIndex();intoutSize=out.size();如果(outSize>0){fireChannelRead(ctx,out,outSize);清除();如果(ctx.isRemoved()){中断;}outSize=0;}SoldState=状态;intoldInputLength=in.readableBytes();try{decodeRemovalReentryProtection(ctx,replayable,out);如果(ctx.isRemoved()){中断;}if(outSize==out.size()){if(oldInputLength==in.readableBytes()&&oldState==state){thrownewDecoderException(StringUtil.simpleClassName(getClass())+".decode()必须消耗入站的"+"数据或者如果它没有解码则改变它的状态任何事物。”);}else{继续;}}}catch(信号重放){replay.expect(REPLAY);如果(ctx.isRemoved()){中断;}//返回检查点(或oldPosition)并重试。intcheckpoint=this.checkpoint;如果(检查点>=0){in.readerIndex(检查点);}else{}中断;}if(oldReaderIndex==in.readerIndex()&&oldState==state){thrownewDecoderException(StringUtil.simpleClassName(getClass())+".decode()方法必须消耗入站数据"+"或者如果它改变它的状态解码了一些东西。”);}如果(isSingleDecode()){中断;}}}catch(DecoderExceptione){抛出e;}catch(异常原因){thrownewDecoderException(原因);}}这里的实现与ByteToMessageDecoder不同,最重要的是在ReplayingDecoder中定义了一个checkpoint。这个检查点设置在尝试解码数据的开始:intoldReaderIndex=checkpoint=in.readerIndex();如果在解码过程中发生异常,使用检查点重置索引:intcheckpoint=this.checkpoint;如果(检查点>=0){in.readerIndex(检查点);}else{}这里捕获的异常是Signal,什么是Signal?信号是一个错误对象:ppublicfinalclassSignalextendsErrorimplementsConstant这个异常是replayable抛出的。replayable是一个名为ReplayingDecoderByteBuf的唯一ByteBuf对象:finalclassReplayingDecoderByteBufextendsByteBuf定义了ReplayingDecoderByteBuf中的Signal属性:此Signal异常是从ReplayingDecoderByteBuf中的get方法抛出的。这里我们以getInt为例,看看异常是如何抛出的:publicintgetInt(intindex){checkIndex(index,4);返回buffer.getInt(index);}getInt方法会先调用checkIndex方法检测buff中的长度,如果小于要读取的长度,会抛出异常REPLAY:privatevoidcheckIndex(intindex,intlength){if(index+length>buffer.writerIndex()){throwREPLAY;}}这就是Signal异常的由来。综上所述,以上就是ReplayingDecoder的介绍。ReplayingDecoder虽然使用方便,但是从它的实现可以看出,ReplayingDecoder通过抛出异常不断重试,所以在一些特殊情况下会造成性能下降。也就是说,在减少我们的代码量的同时,降低了程序的执行效率。想要马跑,又想让马不吃草,这似乎是不可能的。本文已收录于http://www.flydean.com/14-4-netty-replayingdecoder/最流行的解读,最深刻的干货,最简洁的教程,很多你不知道的小技巧等着你等你发现!欢迎关注我的公众号:《程序那些事儿》,懂技术,更懂你!

最新推荐
猜你喜欢