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

Netty系列:CoreMessageToMessageEncoder简介

时间:2023-04-01 23:45:30 Java

innetty在netty中,我们需要传递各种类型的消息。这些消息可以是字符串、数组或自定义对象。不同的对象可能需要相互转换,所以需要一个可以自由转换的转换器。为了统一编码规则,方便用户扩展,netty提供了消息之间相互转换的框架。本文将讲解这个框架的具体实现。框架介绍Netty提供了三个类用于消息与消息之间的转换。这三个类都是抽象类,分别是MessageToMessageDecoder、MessageToMessageEncoder和MessageToMessageCodec。先来看下他们的定义:publicabstractclassMessageToMessageEncoderextendsChannelOutboundHandlerAdapterpublicabstractclassMessageToMessageDecoderextendsChannelInboundHandlerAdapterpublicabstractclassMessageToMessageCodecextendsChannelDuMessageEncoderMessageToMessageDecoder继承自ChannelOutboundHandlerAdapter,负责向channel中写入消息。MessageToMessageDecoder继承自ChannelInboundHandlerAdapter,负责从channel读取消息。MessageToMessageCodec继承自ChannelDuplexHandler,它是一个双向处理程序,可以从通道读取消息或将消息写入通道。有了这三个抽象类,我们再来看这三个类的具体实现。MessageToMessageEncoder首先看消息编码器MessageToMessageEncoder,编码器中最重要的方法是write,看write的实现:try{if(acceptOutboundMessage(msg)){out=CodecOutputList.newInstance();@SuppressWarnings("unchecked")I??cast=(I)msg??;尝试{编码(ctx,投射,输出);}最后{ReferenceCountUtil.release(cast);}if(out.isEmpty()){thrownewEncoderException(StringUtil.simpleClassName(this)+"必须产生至少一条消息。");}}else{ctx.write(msg,promise);}}catch(EncoderExceptione){抛出e;}赶上(Throwablet){抛出新的编码器异常(t);}finally{if(out!=null){try{finalintsizeMinusOne=out.大小()-1;如果(sizeMinusOne==0){ctx.写(出。getUnsafe(0),承诺);}elseif(sizeMinusOne>0){if(promise==ctx.voidPromise()){writeVoidPromise(ctx,out);}else{writePromiseCombiner(ctx,out,promise);}}}最后{out.recycle();}}}}write方法接受一个需要转换的原始对象msg,以及一个表示通道读写进度的ChannelPromise。首先会对msg进行类型判断,在acceptOutboundMessage中实现。publicbooleanacceptOutboundMessage(Objectmsg)throwsException{returnmatcher.match(msg);}这里的matcher是一个TypeParameterMatcher对象,它是在MessageToMessageEncoder构造函数中初始化的一个属性:这里我是要匹配的消息类型。如果不匹配,继续调用ctx.write(msg,promise);将消息写入通道而不进行任何转换以供下一个处理程序调用。如果匹配成功,会调用核心的encode方法:encode(ctx,cast,out);注意encode方法是MessageToMessageEncoder中的一个抽象方法,需要用户在继承类中进行扩展。encode方法实际上是将msg对象转换成需要转换的对象,然后添加到out中。这个out是一个list对象,具体是一个CodecOutputList对象,作为一个list,out是一个可以存储多个对象的list。那么什么时候输出到通道呢?不用担心,在write方法的末尾有一个finally代码块。在此代码块中,out将被写入通道。因为out是一个List,out里面的一些对象可能会写入成功,所以这里需要特殊处理。先判断out中是否只有一个对象,如果是对象,则直接写入通道。如果out中的对象不止一个,则分两种情况。第一种情况是传入的promise是voidPromise,然后调用writeVoidPromise方法。什么是voidPromise?我们知道Promise有多个状态,通过promise的状态变化我们可以知道数据写入的状态。对于voidPromise,它只关心一个失败状态,其他状态不关心。如果用户关心promise的其他状态,会调用writePromiseCombiner方法将多个对象的状态合并为一个promise和return。实际上,在writeVoidPromise和writePromiseCombiner中,out中的对象被一个一个取出来写入到channel中,所以会产生多个promise,需要合并promises:voidPromise=ctx.voidPromise();对于(inti=0;iextendsChannelDuplexHandlerMessageToMessageCodec继承自ChannelDuplexHandler,接收两个泛型参数:INBOUND_IN和OUTBOUND_IN。它定义了两个TypeParameterMatcher,用于过滤inboundMsg和outboundMsg:outboundMsgMatcher=TypeParameterMatcher.find(this,MessageToMessageCodec.class,"OUTBOUND_IN");}分别实现channelRead和write方法读写消息:@OverridepublicvoidchannelRead(ChannelHandlerContextctx,Objectmsg)throwsException{decoder.channelRead(ctx,msg);}@Overridepublicvoidwrite(ChannelHandlerContextctx,Objectmsg,ChannelPromisepromise)throwsException{encoder.write(ctx,msg,promise);这里的decoder和encoder其实就是我们前面提到的MessageToMessageDecoder和MessageToMessageEncoder:rnMessageToMessageCodec.this.acceptOutboundMessage(msg);}@Override@SuppressWarnings("unchecked")protectedvoidencode(ChannelHandlerContextctx,Objectmsg,Listout)throwsException{MessageToMessageCodec.this.encode(ctx,(OUTBOUND_IN)msg??,out);}}};privatefinalMessageToMessageDecoderdecoder=newMessageToMessageDecoder(){@OverridepublicbooleanacceptInboundMessage(Objectmsg)抛出异常{returnMessageToMessageCodec.this.acceptInboundMessage(msg);}@Override@SuppressWarnings("unchecked")protectedvoiddecode(ChannelHandlerContextctx,Objectmsg,Listout)throwsException{MessageToMessageCodec.this.decode(ctx,(INBOUND_IN)msg??,out);}}};可以看到MessageToMessageCodec实际上就是对MessageToMessageDecoder和MessageToMessageEncoder的封装如果需要扩展MessageToMessageCodec,需要实现以下两个方法:protectedabstractvoiddecode(ChannelHandlerContextctx,INBOUND_INmsg,Listout)抛出异常;总结netty中提供的MessageToMessage编码框架是后面扩展codec的基础。只有深入理解其中的原理,才能游刃有余地使用新编解码器。本文已收录于http://www.flydean.com/14-0-1-netty-codec-msg-to-msg/最流行的解读,最深刻的干货,最简洁的教程,很多东西你不知道的小技巧等你来发现!欢迎关注我的公众号:《程序那些事儿》,懂技术,更懂你!