netty系列之:netty中的核心MessageToMessage编码器腾讯云开发者社区

netty为消息和消息之间的转换提供了三个类,这三个类都是抽象类,分别是MessageToMessageDecoder,MessageToMessageEncoder和MessageToMessageCodec。

先来看下他们的定义:

publicabstractclassMessageToMessageEncoderextendsChannelOutboundHandlerAdapter代码语言:javascript复制publicabstractclassMessageToMessageDecoderextendsChannelInboundHandlerAdapter代码语言:javascript复制publicabstractclassMessageToMessageCodecextendsChannelDuplexHandlerMessageToMessageEncoder继承自ChannelOutboundHandlerAdapter,负责向channel中写消息。

MessageToMessageDecoder继承自ChannelInboundHandlerAdapter,负责从channel中读取消息。

MessageToMessageCodec继承自ChannelDuplexHandler,它是一个双向的handler,可以从channel中读取消息,也可以向channel中写入消息。

有了这三个抽象类,我们再看下这三个类的具体实现。

先看一下消息的编码器MessageToMessageEncoder,编码器中最重要的方法就是write,看下write的实现:

publicvoidwrite(ChannelHandlerContextctx,Objectmsg,ChannelPromisepromise)throwsException{CodecOutputListout=null;try{if(acceptOutboundMessage(msg)){out=CodecOutputList.newInstance();@SuppressWarnings("unchecked")Icast=(I)msg;try{encode(ctx,cast,out);}finally{ReferenceCountUtil.release(cast);}if(out.isEmpty()){thrownewEncoderException(StringUtil.simpleClassName(this)+"mustproduceatleastonemessage.");}}else{ctx.write(msg,promise);}}catch(EncoderExceptione){throwe;}catch(Throwablet){thrownewEncoderException(t);}finally{if(out!=null){try{finalintsizeMinusOne=out.size()-1;if(sizeMinusOne==0){ctx.write(out.getUnsafe(0),promise);}elseif(sizeMinusOne>0){if(promise==ctx.voidPromise()){writeVoidPromise(ctx,out);}else{writePromiseCombiner(ctx,out,promise);}}}finally{out.recycle();}}}}write方法接受一个需要转换的原始对象msg,和一个表示channel读写进度的ChannelPromise。

首先会对msg进行一个类型判断,这个判断方法是在acceptOutboundMessage中实现的。

publicbooleanacceptOutboundMessage(Objectmsg)throwsException{returnmatcher.match(msg);}这里的matcher是一个TypeParameterMatcher对象,它是一个在MessageToMessageEncoder构造函数中初始化的属性:

protectedMessageToMessageEncoder(){matcher=TypeParameterMatcher.find(this,MessageToMessageEncoder.class,"I");}这里的I就是要匹配的msg类型。

如果不匹配,则继续调用ctx.write(msg,promise);将消息不做任何转换的写入到channel中,供下一个handler调用。

如果匹配成功,则会调用核心的encode方法:encode(ctx,cast,out);

注意,encode方法在MessageToMessageEncoder中是一个抽象方法,需要用户在继承类中自行扩展。

encode方法实际上是将msg对象转换成为要转换的对象,然后添加到out中。这个out是一个list对象,具体而言是一个CodecOutputList对象,作为一个list,out是一个可以存储多个对象的列表。

那么out是什么时候写入到channel中去的呢?

别急,在write方法中最后有一个finally代码块,在这个代码块中,会将out写入到channel里面。

因为out是一个List,可能会出现out中的对象部分写成功的情况,所以这里需要特别处理。

首先判断out中是否只有一个对象,如果是一个对象,那么直接写到channel中即可。如果out中多于一个对象,那么又分成两种情况,第一种情况是传入的promise是一个voidPromise,那么调用writeVoidPromise方法。

什么是voidPromise呢

我们知道Promise有多种状态,可以通过promise的状态变化了解到数据写入的情况。对于voidPromise来说,它只关心一种失败的状态,其他的状态都不关心。

如果用户关心promise的其他状态,则会调用writePromiseCombiner方法,将多个对象的状态合并为一个promise返回。

事实上,在writeVoidPromise和writePromiseCombiner中,out中的对象都是一个一个的取出来,写入到channel中的,所以才会生成多个promise和需要将promise进行合并的情况:

privatestaticvoidwriteVoidPromise(ChannelHandlerContextctx,CodecOutputListout){finalChannelPromisevoidPromise=ctx.voidPromise();for(inti=0;i

首先也是需要判断读取的消息类型,这里也定义了一个TypeParameterMatcher对象,用来检测传入的消息类型:

protectedMessageToMessageDecoder(){matcher=TypeParameterMatcher.find(this,MessageToMessageDecoder.class,"I");}decoder中重要的方法是channelRead方法,我们看下它的实现:

publicvoidchannelRead(ChannelHandlerContextctx,Objectmsg)throwsException{CodecOutputListout=CodecOutputList.newInstance();try{if(acceptInboundMessage(msg)){@SuppressWarnings("unchecked")Icast=(I)msg;try{decode(ctx,cast,out);}finally{ReferenceCountUtil.release(cast);}}else{out.add(msg);}}catch(DecoderExceptione){throwe;}catch(Exceptione){thrownewDecoderException(e);}finally{try{intsize=out.size();for(inti=0;i

最后在finally代码块中将out中的对象一个个取出来,调用ctx.fireChannelRead进行读取。

消息转换的关键方法是decode,这个方法也是一个抽象方法,需要在继承类中实现具体的功能。

前面讲解了一个编码器和一个解码器,他们都是单向的。最后要讲解的codec叫做MessageToMessageCodec,这个codec是一个双向的,即可以接收消息,也可以发送消息。

先看下它的定义:

publicabstractclassMessageToMessageCodecextendsChannelDuplexHandlerMessageToMessageCodec继承自ChannelDuplexHandler,接收两个泛型参数分别是INBOUND_IN和OUTBOUND_IN。

它定义了两个TypeParameterMatcher,分别用来过滤inboundMsg和outboundMsg:

protectedMessageToMessageCodec(){inboundMsgMatcher=TypeParameterMatcher.find(this,MessageToMessageCodec.class,"INBOUND_IN");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:

privatefinalMessageToMessageEncoderencoder=newMessageToMessageEncoder(){@OverridepublicbooleanacceptOutboundMessage(Objectmsg)throwsException{returnMessageToMessageCodec.this.acceptOutboundMessage(msg);}@Override@SuppressWarnings("unchecked")protectedvoidencode(ChannelHandlerContextctx,Objectmsg,Listout)throwsException{MessageToMessageCodec.this.encode(ctx,(OUTBOUND_IN)msg,out);}};privatefinalMessageToMessageDecoderdecoder=newMessageToMessageDecoder(){@OverridepublicbooleanacceptInboundMessage(Objectmsg)throwsException{returnMessageToMessageCodec.this.acceptInboundMessage(msg);}@Override@SuppressWarnings("unchecked")protectedvoiddecode(ChannelHandlerContextctx,Objectmsg,Listout)throwsException{MessageToMessageCodec.this.decode(ctx,(INBOUND_IN)msg,out);}};可以看到MessageToMessageCodec实际上就是对MessageToMessageDecoder和MessageToMessageEncoder的封装,如果需要对MessageToMessageCodec进行扩展的话,需要实现下面两个方法:

protectedabstractvoidencode(ChannelHandlerContextctx,OUTBOUND_INmsg,Listout)throwsException;protectedabstractvoiddecode(ChannelHandlerContextctx,INBOUND_INmsg,Listout)throwsException;总结netty中提供的MessageToMessage的编码框架是后面对编码解码器进行扩展的基础。只有深入了解其中的原理,我们对于新的编码解码器运用起来才能得心应手。

THE END
1.Java03(面向对象消息和方法参数的传递构造函数this关键字)对象是类的实例 属性 对象所共有的特征 eg.王小红是个对象 王小红有性别、年龄、姓名 这些东西同时也是其他人有的 方法 对象共有的行为 eg. 王小红是一个对象 王小红需要吃饭、睡觉、喝水 吃饭、睡觉、喝水也是其他人拥有的行为 消息和方法 消息 也就是参数 数据是通过方法进行传递的 https://blog.csdn.net/Dean_xiu/article/details/107170485
2.穿越消息之路:深入探讨SpringIntegration的魅力消息转换是Spring Integration中常见的操作,用于将消息从一种格式或结构转换为另一种格式或结构,以满足系统的需求。以下是消息转换的实际应用场景和示例: JSON到对象的转换: @Transformer(inputChannel = "jsonInputChannel", outputChannel = "objectOutputChannel")public MyObject convertJsonToObject(String jsonStringhttps://developer.aliyun.com/article/1436168
3.对象存储POST上传对象存储API文档用户除了可以用PUT直接上传对象外,还可以使用POST上传对象。 单次上传对象大小范围是[0, 5GB],如果需要上传超过5GB的大文件,需要通过多段操作来分段上传。 与PUT上传的区别 PUT上传中参数通过请求头域传递;POST上传则作为消息体中的表单域传递。 PUT上传需在URL中指定对象名;POST上传提交的URL为桶域名,无需指定对象https://ecloud.10086.cn/op-help-center/doc/article/64198
4.解析UML动态建模中消息状态图和顺序图顺序图(SequenceDiagram)用来描述对象之间动态的交互关系,着重体现对象间消息传递的时间顺序。UML动态建模中顺序图存在两个轴:水平轴表示不同的对象,垂直轴表示时间。顺序图中的对象用一个带有垂直虚线的矩形框表示,并标有对象名和类名。垂直虚线是对象的生命线,用于表示在某段时间内对象是存在的。对象间的通信通过在https://www.51cto.com/article/210609.html
5.SAPR/3开发类及表/结构资料SWM 工作流:错误消息 SWO 工作流对象类型 SWP 工作流;处理器 SWR 业务工作流:WAPI 接口 SWT 工作流:工作流跟踪功能 SWU 业务工作流:实用程序功能(无关的设备类型) SWUV 桌面对象 SWW 工作流:工作项 SWWW 基础 WWW 项目的开发类 SWX 商业工作流:实用程序 http://www.360doc.com/content/12/1122/08/132971_249458029.shtml
6.面向对象技术中,对象是类的实例。对象有三种成份:()属性和方法面向对象技术中,对象是类的实例。对象有三种成份:()、属性和方法(或操作)。 A. 标识 B. 规则 C. 封装 D. 消息https://tiku.baidu.com/singledetail/5e87adf9aef8941ea76e0584
7.成都市退役军人及其他优抚对象优待证申领工作公告一方面是符合申领条件的对象很多,基层接受申请需要一定的时间,请多关注各级退役军人事务部门发布的消息,以便安排好申请时间。另一方面,优待证从申请到拿到手,有很多环节,包括建档立卡、申请信息材料核实、审核、制作,以及分送运输到各退役军人服务站等,需要一定的时间。 https://tyjrswj.chengdu.gov.cn/cdstyjrj/c0304/2022-04/13/content_af2618ef18424f6aaf27d1329d569674.shtml
8.这9类优抚对象的抚恤金和生活补助标准又提高啦!赶紧把好消息转给TA赶紧把好消息转给TA 日前,省民政厅发布《关于调整部分优抚对象等人员抚恤和生活补助标准的通知》,从2017年10月1日起调整部分优抚对象等人员抚恤和生活补助标准。 详情如下↓↓ 各类优抚对象补助标准调整情况 1、各等级残疾军人(含伤残人民警察、伤残国家机关工作人员、伤残民兵民工)的残疾抚恤金在原标准的基础上增加10http://app.chuzhou.cn/?action=show&app=article&contentid=336487&controller=article
9.消息分类标准通知消息管理规则推送服务便捷生活类App 运动健康类App 音乐类App 游戏类App 摄影类App 版本更新说明 使用入门 开发服务号消息接口 服务号消息回调通知接口 订单回传接口 批量查询关注者列表接口 查询消息回执接口 实时运动Bundle对象键值 基础能力支持的国家/地区 扩展能力支持的国家/地区 设备接入 说明 运动设备https://developer.huawei.com/consumer/cn/doc/HMSCore-Guides/message-classification-0000001149358835
10.OracleEBSForm个性化开发配置文件:${ps.}消息:${ms.} 插入项目值按钮只获取item对象的值,得到的表达式是常见的 :数据库.数据项形式。例如:OPERATING_UNITS.OPERATING_UNIT 1.验证按钮:判断条件框中的条件是否成立。 2.现在申请按钮:使设置的个性化规则立即生效,不用在重新打开和关闭Form界面,查看个性化规则是否起作用。 https://blog.itpub.net/7580948/viewspace-2214988/
11.Android的消息机制——Handler★第二种情况:消息队列按照消息处理等待时间顺序(从小到大)进行排列。因此插入新消息需要遍历消息队列,找到新消息插入的位置。 2)读取消息(删除消息):next。 4、Handler类:主要负责处理和发送消息。 1)构造方法 构造方法总结:拿到该线程的looper对象、以及和looper对象绑定MQ。 https://www.jianshu.com/p/98e7bb6024fb