RPC调用msgpack序列化问题分析

作者:Alan jiang

时间:2020年6月11日星期四

微信:wei_wei10

问题:

线上业务异常,调用方反馈JSF服务RPC调用异常。读取服务列表服务,所有服务编码均为0(service_code:0)

 

问题分析:

迅速回滚线上代码,立即止损。在CodeReview过程中,发现API接口中的一个参数属性有变化,JSF 的接口jar做了升级。

 

这个参数继承了一个父VO,这个父VO增加了一个新的字段(+ statue:int)。JSF的序列化方式为msgpack。业务方未同步更新JSF接口jar包,导致了JSF 反序列化的异常。

 

 

为什么反序列化会失败?这个和msgpack有关系。

 

咱先来看msgpack的官方定义(官网:https://msgpack.org/):

 

MessagePack is an efficient binary serialization format. It lets you exchange data among multiple languages like JSON. But it's faster and smaller. Small integers are encoded into a single byte, and typical short strings require only one extra byte in addition to the strings themselves.

 

从官方定义中可以得出以下结论:

  1. 首先它是一种二进制序列化格式。
  2. 它允许跨语言交换数据。
  3. 性能上,它比json序列化性能要好。
  4. 体积上,它比json序列化体积要小。

 

以上是msgpack的优点,但是在接受它的优点的同时,必须同时接受它的缺点。

其中一个缺点是无法改变字段顺序

 

对,无法改变字段顺序。线上JSF调用,反序列化异常就是因为这个原因。序列化的对象,顺序被改变了。

 

我找来了一个官方的一个示例说明,这个例子里解释了为什么序列化后内容体积会变小。

 

 

可以看到,msgpack序列化结果,只有value!并且value进行了专属映射。

(官方说明书:https://github.com/msgpack/msgpack/blob/master/spec.md

 

再来个直观对比:

 

 

 

msgpack是不是让人又爱又恨。选用msgpack就是为了效率,咱来了解一下兼容规则。

 

在两边不同时升级的情况下,字段兼容规则如下:

  1. 不能调整原有字段顺序,不能删减字段。最后一个字段除外。
  2. 新增的字段必须在字段最后。
  3. 父类字段不能改变,因为父类改变等于在子类中间插入了一个字段。

 

如果违反上面👆任意一条,必须RPC客户端与服务器同时升级。

**MessagePack(简称MsgPack)** 是一种高效的二进制序列化格式,设计用于跨语言数据交换(如前后端通信、微服务调用等)。以下是其核心特性和详解: --- ### **1. 核心特点** | 特性 | 说明 | |---------------------|----------------------------------------------------------------------| | **二进制格式** | 比JSON/XML更紧凑,节省带宽和存储空间(通常小30%~50%)。 | | **跨语言支持** | 支持50+语言(如Java、Python、JS、Go等),类似Protocol Buffers但无需IDL。 | | **动态类型** | 不依赖预定义模式(Schema-less),使用时类似JSON。 | | **高性能** | 序列化/反序列化速度通常比JSON快2~10倍(依赖具体实现)。 | --- ### **2. 与JSON的对比** | 对比项 | MessagePack | JSON | |---------------------|--------------------------------------|-----------------------------------| | **数据格式** | 二进制 | 文本(UTF-8) | | **可读性** | 不可直接阅读 | 可直接阅读 | | **数字处理** | 支持更高效的数字编码(如变长整数) | 所有数字均为文本形式 | | **扩展类型** | 支持二进制数据(`byte[]`)、扩展类型 | 仅基本类型(需Base64编码二进制) | --- ### **3. 使用场景** - **高并发微服务**:替代JSON提升RPC性能(如gRPC+MsgPack)。 - **物联网设备通信**:减少嵌入式设备的带宽消耗。 - **缓存序列化**:Redis等缓存系统的高效存储格式。 --- ### **4. Java示例** #### **(1) 添加依赖(Maven)** ```xml <dependency> <groupId>org.msgpack</groupId> <artifactId>msgpack-core</artifactId> <version>0.9.3</version> </dependency> ``` #### **(2) 序列化/反序列化** ```java import org.msgpack.core.MessagePack; import org.msgpack.core.MessageBufferPacker; import org.msgpack.core.MessageUnpacker; // 序列化对象 → 二进制 MessageBufferPacker packer = MessagePack.newDefaultBufferPacker(); packer.packInt(42) .packString("Hello MsgPack") .packArrayHeader(2).packString("A").packString("B"); byte[] binaryData = packer.toByteArray(); // 反序列化二进制 → 对象 MessageUnpacker unpacker = MessagePack.newDefaultUnpacker(binaryData); int num = unpacker.unpackInt(); // 42 String str = unpacker.unpackString(); // "Hello MsgPack" unpacker.unpackArrayHeader(); // 读取数组长度2 String a = unpacker.unpackString(); // "A" String b = unpacker.unpackString(); // "B" ``` --- ### **5. 性能优化建议** - **复用Packer/Unpacker**:避免频繁创建对象(类似线程池思想)。 - **批处理**:对大列表序列化时,优先打包为单条二进制数据。 - **压缩**:对二进制结果进一步用Snappy/Gzip压缩(尤其文本多的场景)。 --- ### **6. 与其他序列化对比** | 工具 | 是否需要Schema | 二进制/文本 | 典型用途 | |----------------|----------------|-------------|-----------------------------| | **MessagePack** | ❌ 否 | 二进制 | 通用数据交换 | | **Protocol Buffers** | ✅ 是 | 二进制 | 强类型RPC(如gRPC) | | **JSON** | ❌ 否 | 文本 | Web API、配置文件 | | **Avro** | ✅ 是 | 二进制 | Hadoop生态、大数据存储 | ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小蒋聊技术

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值