范文健康探索娱乐情感热点
投稿投诉
热点动态
科技财经
情感日志
励志美文
娱乐时尚
游戏搞笑
探索旅游
历史星座
健康养生
美丽育儿
范文作文
教案论文
国学影视

netty系列之netty中的核心MessageToMessage编码器

  简介
  在netty中我们需要传递各种类型的消息,这些message可以是字符串,可以是数组,也可以是自定义的对象。不同的对象之间可能需要互相转换,这样就需要一个可以自由进行转换的转换器,为了统一编码规则和方便用户的扩展,netty提供了一套消息之间进行转换的框架。本文将会讲解这个框架的具体实现。框架简介
  netty为消息和消息之间的转换提供了三个类,这三个类都是抽象类,分别是MessageToMessageDecoder,MessageToMessageEncoder和MessageToMessageCodec。
  先来看下他们的定义:public abstract class MessageToMessageEncoder extends ChannelOutboundHandlerAdapter 复制代码public abstract class MessageToMessageDecoder extends ChannelInboundHandlerAdapter 复制代码public abstract class MessageToMessageCodec extends ChannelDuplexHandler  复制代码
  MessageToMessageEncoder继承自ChannelOutboundHandlerAdapter,负责向channel中写消息。
  MessageToMessageDecoder继承自ChannelInboundHandlerAdapter,负责从channel中读取消息。
  MessageToMessageCodec继承自ChannelDuplexHandler,它是一个双向的handler,可以从channel中读取消息,也可以向channel中写入消息。
  有了这三个抽象类,我们再看下这三个类的具体实现。MessageToMessageEncoder
  先看一下消息的编码器MessageToMessageEncoder,编码器中最重要的方法就是write,看下write的实现:    public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {         CodecOutputList out = null;         try {             if (acceptOutboundMessage(msg)) {                 out = CodecOutputList.newInstance();                 @SuppressWarnings("unchecked")                 I cast = (I) msg;                 try {                     encode(ctx, cast, out);                 } finally {                     ReferenceCountUtil.release(cast);                 }                  if (out.isEmpty()) {                     throw new EncoderException(                             StringUtil.simpleClassName(this) + " must produce at least one message.");                 }             } else {                 ctx.write(msg, promise);             }         } catch (EncoderException e) {             throw e;         } catch (Throwable t) {             throw new EncoderException(t);         } finally {             if (out != null) {                 try {                     final int sizeMinusOne = out.size() - 1;                     if (sizeMinusOne == 0) {                         ctx.write(out.getUnsafe(0), promise);                     } else if (sizeMinusOne > 0) {                         if (promise == ctx.voidPromise()) {                             writeVoidPromise(ctx, out);                         } else {                             writePromiseCombiner(ctx, out, promise);                         }                     }                 } finally {                     out.recycle();                 }             }         }     } 复制代码
  write方法接受一个需要转换的原始对象msg,和一个表示channel读写进度的ChannelPromise。
  首先会对msg进行一个类型判断,这个判断方法是在acceptOutboundMessage中实现的。    public boolean acceptOutboundMessage(Object msg) throws Exception {         return matcher.match(msg);     } 复制代码
  这里的matcher是一个TypeParameterMatcher对象,它是一个在MessageToMessageEncoder构造函数中初始化的属性:    protected MessageToMessageEncoder() {         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进行合并的情况:    private static void writeVoidPromise(ChannelHandlerContext ctx, CodecOutputList out) {         final ChannelPromise voidPromise = ctx.voidPromise();         for (int i = 0; i < out.size(); i++) {             ctx.write(out.getUnsafe(i), voidPromise);         }     }      private static void writePromiseCombiner(ChannelHandlerContext ctx, CodecOutputList out, ChannelPromise promise) {         final PromiseCombiner combiner = new PromiseCombiner(ctx.executor());         for (int i = 0; i < out.size(); i++) {             combiner.add(ctx.write(out.getUnsafe(i)));         }         combiner.finish(promise);     } 复制代码MessageToMessageDecoder
  和encoder对应的就是decoder了,MessageToMessageDecoder的逻辑和MessageToMessageEncoder差不多。
  首先也是需要判断读取的消息类型,这里也定义了一个TypeParameterMatcher对象,用来检测传入的消息类型:    protected MessageToMessageDecoder() {         matcher = TypeParameterMatcher.find(this, MessageToMessageDecoder.class, "I");     } 复制代码
  decoder中重要的方法是channelRead方法,我们看下它的实现:    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {         CodecOutputList out = CodecOutputList.newInstance();         try {             if (acceptInboundMessage(msg)) {                 @SuppressWarnings("unchecked")                 I cast = (I) msg;                 try {                     decode(ctx, cast, out);                 } finally {                     ReferenceCountUtil.release(cast);                 }             } else {                 out.add(msg);             }         } catch (DecoderException e) {             throw e;         } catch (Exception e) {             throw new DecoderException(e);         } finally {             try {                 int size = out.size();                 for (int i = 0; i < size; i++) {                     ctx.fireChannelRead(out.getUnsafe(i));                 }             } finally {                 out.recycle();             }         }     } 复制代码
  首先检测msg的类型,只有接受的类型才进行decode处理,否则将msg加入到CodecOutputList中。
  最后在finally代码块中将out中的对象一个个取出来,调用ctx.fireChannelRead进行读取。
  消息转换的关键方法是decode,这个方法也是一个抽象方法,需要在继承类中实现具体的功能。MessageToMessageCodec
  前面讲解了一个编码器和一个解码器,他们都是单向的。最后要讲解的codec叫做MessageToMessageCodec,这个codec是一个双向的,即可以接收消息,也可以发送消息。
  先看下它的定义:public abstract class MessageToMessageCodec extends ChannelDuplexHandler 复制代码
  MessageToMessageCodec继承自ChannelDuplexHandler,接收两个泛型参数分别是INBOUND_IN和OUTBOUND_IN。
  它定义了两个TypeParameterMatcher,分别用来过滤inboundMsg和outboundMsg:    protected MessageToMessageCodec() {         inboundMsgMatcher = TypeParameterMatcher.find(this, MessageToMessageCodec.class, "INBOUND_IN");         outboundMsgMatcher = TypeParameterMatcher.find(this, MessageToMessageCodec.class, "OUTBOUND_IN");     } 复制代码
  分别实现了channelRead和write方法,用来读写消息:    @Override     public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {         decoder.channelRead(ctx, msg);     }      @Override     public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {         encoder.write(ctx, msg, promise);     } 复制代码
  这里的decoder和encoder实际上就是前面我们讲到的MessageToMessageDecoder和MessageToMessageEncoder:    private final MessageToMessageEncoder encoder = new MessageToMessageEncoder() {          @Override         public boolean acceptOutboundMessage(Object msg) throws Exception {             return MessageToMessageCodec.this.acceptOutboundMessage(msg);         }          @Override         @SuppressWarnings("unchecked")         protected void encode(ChannelHandlerContext ctx, Object msg, List out) throws Exception {             MessageToMessageCodec.this.encode(ctx, (OUTBOUND_IN) msg, out);         }     };      private final MessageToMessageDecoder decoder = new MessageToMessageDecoder() {          @Override         public boolean acceptInboundMessage(Object msg) throws Exception {             return MessageToMessageCodec.this.acceptInboundMessage(msg);         }          @Override         @SuppressWarnings("unchecked")         protected void decode(ChannelHandlerContext ctx, Object msg, List out) throws Exception {             MessageToMessageCodec.this.decode(ctx, (INBOUND_IN) msg, out);         }     }; 复制代码
  可以看到MessageToMessageCodec实际上就是对MessageToMessageDecoder和MessageToMessageEncoder的封装,如果需要对MessageToMessageCodec进行扩展的话,需要实现下面两个方法:    protected abstract void encode(ChannelHandlerContext ctx, OUTBOUND_IN msg, List out)             throws Exception;      protected abstract void decode(ChannelHandlerContext ctx, INBOUND_IN msg, List out)             throws Exception; 复制代码总结
  netty中提供的MessageToMessage的编码框架是后面对编码解码器进行扩展的基础。只有深入了解其中的原理,我们对于新的编码解码器运用起来才能得心应手。
呼伦贝尔宋Pro让利促销,直降1。0万元,欢迎垂询刚刚小编在车友圈看到好多朋友都在讨论宋Pro直降10。78,这么诱人的降价幅度,想要购车的朋友们不考虑一下趁机拿下?比亚迪益丰祥泰店,活动时间截止到09月28日,机会难得,不容错过评述丨新思科技BSIMM助力OPPO找到安全坐标,构建整体可信工程层出不穷的安全问题和软件漏洞,让企业对软件安全的重视度提升到了前所未有的高度。在整个软件生命周期中,无论是敏捷开发还是DevOps流程,安全性已经贯穿始终,成为至关重要的一环。那么上海苏宁易购公布国庆消费大数据三胎经济带火大容量家电销量家电5折购百款新品上市一亿件超低好货,国庆黄金周,助推苏宁易购线上线下全面爆发。国庆假期期间,上海苏宁易购到店客流及成交额均创新高,整体客流大幅度增长超170。同时,由于iphon中秋佳节碰撞智慧科技你好BOE美好生活馆亮相上海9月21日,作为BOE(京东方)年度品牌巡展的第四站,你好BOE美好生活馆在花好月圆的中秋佳节正式亮相上海环球港,在这座既古老又现代的摩登都会打造一座融科技感艺术感潮流风尚于一体的粒界科技图形引擎再开内测助力数字化转型2021年9月28日,图形技术提供商粒界科技在上海举办了以图有形粒无界为主题的产品发布会,本次发布会对粒界渲染技术的应用场景,实际效能做了重点讲解,对以图形引擎为中枢的数字生活未来比亚迪新能源自主研发科技才是硬道理2003年开始研发,2008年正式上市的混合动力车型比亚迪F3DM,是中国品牌首款量产插电式混合动力汽车。这款具有里程碑式意义的车型奠定了比亚迪在中国新能源汽车市场中的开拓者和引领比亚迪8月销量稳增18月新能源车零售147。9万辆,同比增长202。1。与传统燃油车走势形成强烈差异化的特征,实现对燃油车市场的替代效应,并拉动车市向新能源化转型的步伐。主流合资品牌中的南北大众的新专访瑞能半导体MarkusMosen深化功率半导体全产业布局从恩智浦功率产品线独立出来,一度缺乏仓库和供应链等基础设施,到如今成为全球功率半导体的领导厂商,碳化硅市场份额在国际上名列前茅,并拥有完备的供应链管理。所有的这一切跨越式发展,瑞能你以为戴上口罩就可以彻底防住病毒了吗?别忘了你还有一双眼睛你以为戴上口罩就可以彻底防住病毒了吗?别忘了你还有一双眼睛新冠肆虐已经有一年之久了,国内疫情的偶尔反复和国外疫情的持续发展势头,都好像在宣告着我们将与这类RNA病毒打上一场持久战,二十国集团工商界领袖聚会为什么单单邀请这家中国企业分享经验2019届B20论坛(二十国集团工商峰会)在东京拉开了帷幕,作为G20机制的重要配套活动,B20是各国工商界参与全球经济治理和推动世界经济增长的重要平台。本届G20大阪峰会的议题聚不吹不黑中国5G商用到底比美国强在哪?6月6日,工信部宣布向4家企业颁发了5G电信业务经营许可证,这标志着中国正式开启了5G商用的步伐。而在今年4月,韩国和美国电信运营商先后宣布推出5G商用服务。5G商用意味着它已经从
ITX夏日能否HOLD得住9900K风水结合LD03小钢炮装机展示说在前面其实对于这次装机完全是自己出于新奇心的一次瞎折腾,个人真正开始热衷于折腾电脑硬件就是始于银欣的这个结构机箱,初款的乌鸦1对于当初的机箱产业来说可谓是惊艳,颠覆传统机箱的结构关于海燕T241收音机的打理改造想法国庆期间把家父这台T241打理完毕,遂产生了一些改机想法,目前还只是想法,将会不日开工,付诸实践,让老机更好的发挥余热,成功后再详细发文总结。交流声重。该机型使用220V供电,变压德仕博1780简评与改造我之前对德仕博这个品牌毫无所知,猜想应该是东莞的某个小厂,1780应该是出口转内销的机型,因为我看到过国外同款马甲机型,再加上是DSP机,所以并不怎么关注。但是身边却一直有朋友推荐索尼SW77的打理维修经验SW77是索尼90年代初的高端数调机,无论从外观,功能还是内部电路工艺都充满了设计感,有些技术甚至可以说是激进的,贴片电解电容在收音机上的使用就是一个例子。贴片电容体积小,可以提高索尼SW77调幅AGC的起控点调整我之前有发过SW77的电路分析,文中提到它的调幅电路有完善的AGC控制措施,可以根据电台信号强度自动调整高放级增益而不是简单的衰减信号,这种手段似乎更科学合理一些。它的控制信号取自SONYICF5900W电路分析前些天我分享了松下名机RF2200的电路特点,由此自然而然想到了它同期的对飙机型5波段的索尼ICF5900(W)。两台机同属于BCL经典机,同发布于1975年前后只相差几个月。作为索尼CRF160电路分析索尼的便携机系列被命名为ICF,口袋耳机随身听为SRF,CRF系列是桌面级的专业收音机,因此灵敏度和选择性,音质等指标都堪称优秀。这台13波段的CRF160发布于70年代初,采用全一台FM立体声收音机的制作手头多年积攒的各种零件实在太多,扔了又可惜,只能想办法利用了,于是有了这台调频立体声收音机。外壳是网上买的防水盒,价格便宜记得只要十来块钱但是光泽度好坚固耐用,最大好处是易于手工加索尼便携机的同步检波单边带解调分析索尼常见的几款经典数调便携机均支持同步检波和单边带解调,例如SW77,SW07,SW7600G,SW7600GR,SW100,EX5等。当年这些高端功能在模拟便携机上实现是很不容易德生PL660的中波假响应和啸叫分析我之前陆陆续续发了一些德生PL660中波某频点假响应或啸叫的现象视频和初步分析,随着验证的深入,这里总结整理一下我发现这个问题的来龙去脉和原因分析。起初发现啸叫问题,我还怀疑是我这德生PL600电路分析德生PL600电路分析我之前写过德生9700DX,450,550,660,330,990,501,S2000的电路分析文章,因为它们都极具代表性9700DX五年磨一剑有着漂亮的刻度