996 工作态,晚上上线,业务调用方反馈通过 JSF(Jingdong Service Framework)RPC 调用的返回参数异常,获取服务列表的所有服务 service_status 为 0(0 表示已删除)。

系统迅速回滚,返回参数正常。CodeReview 发现 API 的 Vo 父对象新增了属性,而 RPC 传输的子 Vo 继承了父 Vo,虽然,新增的属性是放在父 Vo 的末尾,但为 JSF 的序列化方式为 msgpack,由于业务调用方未更新父 Vo,所以导致了子 Vo 反序列化的异常。

为什么 Vo 的反序列化会失败?而不是像 JSON、Hession 可以向下兼容?

首先,我们要先了解一下 msgpack 的原理。

msgpack

msgpack 用官方的话说,msgpack 是一种高效的二进制序列化方式。

基于官方的解释:JSON 为什么会变小?

核心压缩方式可参看官方说明 messagepack specification:

https://github.com/msgpack/msgpack/blob/master/spec.md

简单的总结下,msgpack 是按字段顺序进行序列化和反序列化的,优点是速度快,缺点是无法改变字段顺序。

msgpack 字段兼容规则

在两边不同时升级的情况下,字段兼容规则如下:(包括 Bean 和枚举)

  • 1、不能调整原有字段顺序,不能删减字段,除非是删最后一个字段

  • 2、新加的字段必须在字段最后面(只是字段顺序,不是文件最后面,getter/setter 方法等随意)

  • 3、父类的字段不能变,因为父类一变相当于子类的中间插入一个字段

满足上面规则,服务端和客户端哪边先升级都无所谓。

如果是需要父类加字段,或者中间加减字段这种,则需要服务端和调用端同时升级。

基于此的理解,我们在看下类继承导致 RPC 调用 msgpack 序列化问题分析:

msgpack VS hessian

Hessian 序列化的时候,会写入字段名称,然后字段值,可以想象为一个 map。

MsgPack 序列化的时候,不写入字段名字,会按字段顺序写入值,可以想象为一个数组。

方案一:Vo 不采用继承,对 Vo 设置 final 属性

方案二:不使用 msgpack 序列化方式,改成 hessian 序列化方式

其实,之前使用 msgpack 序列化方式也出现过类似的问题,如在 Vo 中间新增属性,导致序列化失败,这次的问题之所以没有异常,是因为父 Vo 中新增的属性也是 int 类型,这导致序列化过程中虽然没有赋值,但采用里默认值 0。所以,并没有出现异常,而是结果值错误。

RPC 框架在分布式系统中担任着越来越重要的角色,如阿里的 duboo、京东的 jsf、谷歌的 gRPC,序列化的方式也多种多样,如 json、hessian、msgpack、protobuf 等等,这也需要我们在使用过程中了解其序列化的原理,不断精进。

感谢张松然对本文章的校订。

Msg Pack Serialization 该项目不再维护。 有关Message Pack 格式的Swift Codable实现,请参见 。 Msg Pack Serialization按照Foundation的NSJSONSerialization 的API约定在Objective-C对象和数据之间进行编码和解码。 id obj = @{ @" foo " : @( 42.0 ), @" bar " : @" lorem ipsum " , @" baz " : @[@ 1 , @ 2 , @ 3 , @ 4 ] NSError *error = nil ; CFAbsoluteTime t_0 = CFAbsoluteTimeGetCurrent(); NSData *data = [ Msg Pa 可以从以下目录运行示例,以获取数据 示例(使用localhost作为Redis主机): >>> python3.7 - m examples.dataclass.server localhost >>> python3.7 - m examples.dataclass.client localhost >>> docker - compose run pytest -- cov = asyncio_ rpc -- cov - report = html 或消息包or msg pack 是一个用于 Python 的快速 msg pack 库。它是orjson的 fork/reboot 它的 序列化 速度比 msg pack -python快,反 序列化 速度稍慢(现在)。它支持的 序列化 :数据 ,日期时间,numpy的,pydantic和UUID实例本身。与其他 Python msg pack 库相比,它的特点和缺点:本机 序列化 dataclass实例。将datetime 、date和time实例 序列化 为 RFC 3339 格式,例如,“1970-01-01T00:00:00+00:00”本地和更快地 序列化 numpy.ndarray实例。本机 序列化 pydantic.BaseModel实例(忽略配置 ATM)。使用default钩子 序列化 任意 型or msg pack 支持 CPython 3.6、3.7、3.8、3.9 和 3.10。or msg pack 不支持 PyPy。发布遵循语义版本控制,并且在没有选择加入标志的情况下 序列化 新对象 型被认为是重大更改。or msg pack 在 Apache 2.0 和 MIT 许可下获得许可。存储库和 问题 跟踪器是github.co protobuf(protocol buffer) 是谷歌内部的混合语言数据标准。通过将结构化的数据进行 序列化 (串行化),用于通讯协议、数据存储等领域和语言无关、平台无关、可扩展的 序列化 结构数据格式。我们说的protobuf一种二进制数据交换格式。可以将 C++ 中定义的存储 的内容与二进制序列串相互转换,主要用于数据传输或保存定义了一种源文件,扩展名为.proto( 比.cpp文件),使用这种源文件,可以定义存储 的内容protobuf有自己的编译器protoc,可以将.proto编译成.cc。 msg pack 简介 Msg Pack 是一种高效的二进制 序列化 格式。官方对 msg pack 地道的介绍:——It's like JSON,but fast and small. 像JSON一样,跨平台、跨操作系统、支持多种语言,在多种语言之间使用,高效压缩。 msg pack 会将数据打包成二进制的. 目录1. AirSim 的settings的配置文件及概要说明(1)json文件的存放地址(2) 关联小课堂:JSON语法(3) settings.json中的配置参数2. AirSim 的settings的主要配置说明 1. AirSim 的settings的配置文件及概要说明 (1)json文件的存放地址 AirSim 安装后会有一个 subfolder , 其中有一个文件为 settings.json。AirSim subfolder 的地方为: Windows:Documents\AirSim on 在定义protobuf消息时,有时候需要用到extensions来对原有的消息 型进行扩展,有利于消息定义的重复使用。1、下面写一个最简单的例子,定义一个message BaseData,并对其进行扩展:Example.proto:// 定义一个message BaseData,100~199之间的tag可供扩展message BaseData {required int32 code = 1;e... 对于 序列化 ,有必要掌握它的原理,这样在实际运用过程中,出现 问题 就能快速解决掉。 阿里的dubbo默认使用的就是hessian 序列化 ,它的特性是: 序列化 的时候,会写入字段名称,然后字段值,你可以想象为一个map。 而京东jsf默认使用的是 msg pack 序列化 ,它的特性是: 序列化 的时候,不写入字段名字,会按字段顺序写入值,你可以想象为一个数组。 从这里可以...