ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

netty之二次解码MessageToMessageDecoder

2021-07-27 23:32:05  阅读:329  来源: 互联网

标签:netty 解码 PARSER MessageToMessageDecoder extensionRegistry getParserForType proto


目录

二次解码

ObjectEncoder 如何实现编码

1、 writeStreamHeader

2、writeClassDescriptor

总结

protobuf的编码解码过程

1、ProtobufVarint32FrameDecoder

2、ProtobufDecoder

3、ProtobufVarint32LengthFieldPrepender

4、ProtobufEncoder

总结


二次解码

我们将数据帧 转为正确的byte数组 我们称之为一次解码

将byte数据流转换为java对象 , 我们称之为二次解码

img

一次解码是将数据正确的拆分或者拼合 , 二次解码是将数据正确的序列化或者反序列化 。

这里用到了分层的思想 , 一边后续协议变更时,高耦合度代码修改量大的问题

img

ObjectEncoder 如何实现编码

进入源码如图所示的包路径,第48行就是编码的核心方式,看得出来使用的是CompactObjectOutputStream的方式,也就是压缩了数据

img

1、 writeStreamHeader

writeStreamHeader简化了STREAM_MAGIC字段

jdk自带的writeStreamHeader()方法中带有STREAM_MAGIC来标志当前协议类型,CompactObjectOutputStream类没有,用于节省空间

下图是java的jdk源码:

img

img

2、writeClassDescriptor

CompactObjectOutputStream对非原始数据类型,写入类的描述信息只有两行代码

img

那么,既然这么多的描述信息都被删除了,那怎么实现反序列化呢? 关键就在于48行,写入了一个类的名称。同时看到下图左上角,包内有一个ClassLoader,它是利用反射去实现反序列化的。

img

总结

ObjectEncoder简化了魔术和元信息来减少数据的传输,使用反射的方式实现对原来的对象的反序列化。

protobuf的编码解码过程

WorldClockClientInitializer是netty中提供一个整合protobuf例子,提供了四个编码解码器

img

1、ProtobufVarint32FrameDecoder

ProtobufVarint32FrameDecoder是一个类似于LengthFieldBasedFrameDecoder,用一个字段存储length,然后data字段存储数据。 它做出的改进是,会根据根据数据的长度使用readRawVarint32方法去按照varint32的方式去读取头部的length长度。里面的int字段可变长度的就是他做出的改进。

img

2、ProtobufDecoder

ProtobufDecoder核心步骤: 1.类初始化的时候,判断是否含所有getParserForType方法,如果有HAS_PARSER为true 2.decode方法中,如果带有extension则按照扩展的解析方式解析,如果没有根据HAS_PARSER字段判断是mergeFrom()还是parseFrom()解析

@Sharable
public class ProtobufDecoder extends MessageToMessageDecoder<ByteBuf> {
​
    // HAS_PARSER是版本大于2.5.0才有的方法,旧版本的protobuf没有该方法
    // HAS_PARSER用于标志protobuf中是否有getParserForType方法
    private static final boolean HAS_PARSER;
​
    static {
        boolean hasParser = false;
        try {
            // MessageLite.getParserForType() is not available until protobuf 2.5.0.
            MessageLite.class.getDeclaredMethod("getParserForType");// 反射获取getParserForType方法
            hasParser = true;
        } catch (Throwable t) {
            // Ignore
        }
​
        HAS_PARSER = hasParser;
    }
​
    private final MessageLite prototype;
    private final ExtensionRegistryLite extensionRegistry;
​
    /**
     * Creates a new instance.
     */
    public ProtobufDecoder(MessageLite prototype) {
        this(prototype, null);
    }
​
    public ProtobufDecoder(MessageLite prototype, ExtensionRegistry extensionRegistry) {
        this(prototype, (ExtensionRegistryLite) extensionRegistry);
    }
​
    public ProtobufDecoder(MessageLite prototype, ExtensionRegistryLite extensionRegistry) {
        this.prototype = ObjectUtil.checkNotNull(prototype, "prototype").getDefaultInstanceForType();
        this.extensionRegistry = extensionRegistry;
    }
​
    @Override
    protected void decode(ChannelHandlerContext ctx, ByteBuf msg, List<Object> out)
            throws Exception {
        final byte[] array;
        final int offset;
        final int length = msg.readableBytes();
        if (msg.hasArray()) {
            array = msg.array();
            offset = msg.arrayOffset() + msg.readerIndex();
        } else {
            array = ByteBufUtil.getBytes(msg, msg.readerIndex(), length, false);
            offset = 0;
        }
​
        // 带有extension字段则按照扩展的规则进行解析
        if (extensionRegistry == null) {
            // HAS_PARSER表示是否含有getParserForType方法
            if (HAS_PARSER) {
                // 使用parseFrom方法解析
                out.add(prototype.getParserForType().parseFrom(array, offset, length));
            } else {
                // 使用mergeFrom方法解析
                out.add(prototype.newBuilderForType().mergeFrom(array, offset, length).build());
            }
        } else {
            if (HAS_PARSER) {
                out.add(prototype.getParserForType().parseFrom(
                        array, offset, length, extensionRegistry));
            } else {
                out.add(prototype.newBuilderForType().mergeFrom(
                        array, offset, length, extensionRegistry).build());
            }
        }
    }
}

3、ProtobufVarint32LengthFieldPrepender

通过可变数据类型的规则解析body的数据长度

img

4、ProtobufEncoder

将数据转化为字节数据用于传传输

img

总结

protobuf对数据长度处理使用可变数据类型,减少数据的传输压力。

标签:netty,解码,PARSER,MessageToMessageDecoder,extensionRegistry,getParserForType,proto
来源: https://blog.csdn.net/u012391423/article/details/119155698

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有