这是我参与2022首次更文挑战的第20天,活动详情查看:2022首次更文挑战
数据编码与演化(系统兼容性与可扩展性)
这里强调的是应用开发的时候往往是一个逐步演进的过程,如何更好的保证数据的双向兼容性,需要细心的设计
这里的数据包括很多方面,存储、沟通都有
模式可以看作是一种数据的结构
数据编码形式
主要有两大类:
- 程序内部的数据使用:包括各类的数据结构,针对CPU访问做了优化
- 数据写入文件或者网络传输交互的时候,编码为某种自包含的字节编码
两者相互转换的过程被称为是序列化与反序列化过程
第一类的实际实现常常与语言本身以及操作系统平台所挂勾,但是在实际数据传输与跨平台中,需要有一个共识级别的标准化编码
可读性较强的编码
其中的佼佼者为XML和JSON
缺点
- 数字与字符串不能很好地区分,整数与浮点数的精度问题,没有办法表示大数
- 不支持二进制字符回传,不支持字符编码字节序列
- others
二进制编码
占用较小的空间
二进制编码是标准编码的一种编码形式
在json、xml下产生了很多二进制编码方式,如MessagePack等,不同编码方式下的二进制数据的组织形式不一样
\
一般过程为:
不同的编程语言(机器读取的结构)《——》二进制编码工具、协议《——》二进制编码
中间的工具与协议一般有对应的代码生成工具生成不同编程语言里面对应模式的类
Protocol Buffer与Thrift
Thrift使用IDL来描述模式
struct Person {
1: required string userName,
2: optional i64 number,
3: optional list<string> interests,
}
Protocol Buffer类似
message Person{
required string user_name = 1;
optional int64 number = 2;
repeated string interests;
}
- 两者都是通过数字编码的方式来标记字段对应关系
- 二进制编码中都记录了数字编码、字段类型、长度等信息,会在解码的时候用到
- required与optional并不会在编码中体现,其作用是在解码的时候进行检查,如果没有required的字段数据会出错
模式演进带来的兼容性问题
添加新的字段:增加数字编码即可,标注为optional,就能保证就代码的兼容性问题
修改字段名称:如果不变动数字,没有影响
修改字段类型:会面临不同的版本模式读取的时候,数据截断的问题
删除字段:可以删除optional的字段
Avro
也是一种二进制编码格式。Thrift不适合Hadoop的用例(主要体现在什么方面呢?)
有两种模式:IDL(人工编辑)、基于JSON(易于机器读取)
-
相比于Thrift与Protocol,没有使用编号
-
编码中只标记了长度和值,没有类型和编号,最为紧凑,在解码的过程中是通过顺序便利的方式进行解码的\
-
-
如何识别数据类型和对应字段?\
-
- 编码与解码涉及到两个模式,一个是写模式即用于编码的模式,一个是读模式即用于解码的模式
- 写模式与读模式不一定要一样,Avro通过模式库的方式查看两者差异,并且将两种模式进行转换,如果读模式出现写模式中未出现的字段,会用默认值填充
- 写模式可以通过在编码的时候在最开头标记写模式版本即可
-
\
\
模式演进带来的兼容性问题
因为Avro涉及到了模式的版本问题,因此可以更加清晰地理解向前与向后兼容的表现形式
向前兼容:新版本模式作为写模式,旧版本模式作为读模式
- 与上文的其他二进制编码方式类似,删除的字段需要保证就旧模式该字段有默认值
向后兼容:新版本模式作为读模式,旧版本模式作为写模式
- 新模式增加的字段应该具有默认值,防止读取旧模式没有的字段而出错
对动态生成的模式更加友好
没有字段标注的特性可以更加容易适配数据之间模式的变动,如数据库的模式变动,增加或者加少了一列,前后根据不同的Avro模式导出的数据之间依旧可以进行自动无冲突转换,对于Thrift需要人工干预增加标记或者减少标记,实际上就是通过版本比对的方式由计算机完成了字段关系的匹配
数据流模式
进程之间会通过各种手段进行数据的传输,如网络、文件等,这些过程都涉及到将数据编码为字节序列
- 通过数据库
- 通过rpc、rest
- 通过异步消息传递
这里也从这三个方面讨论编码的兼容问题
数据库
如新旧字段由新旧代码读取、写入时可能带来的不同的影响
-
旧版本的代码写入数据的时候可能会影响新代码在新字段写入的数据\
-
- 大多数数据库支持简单的模式修改,如不影响就数据的情况下增加具有默认值的新列
\
数据归档
在备份和数据欢颜的时候都会用到数据库快照,不同版本创建的数据快照模式可能被不一样,这里使用Avro可以兼容不同的模式进行转存等操作
REST和RPC(基于服务的数据流)
两者都是运用于不断端之间的沟通,REST常用于各种Web服务器,RPC则常用于各种微服务间的调用信息传递
网络服务
HTTP作为底层协议,常被称为Web服务(但是实际上HTTP不仅仅是用于Web服务,也能用于各种服务之间的沟通交流),有两种流行的Web组织方法:REST和SOAP
REST:一种基于HTTP原则的设计理念,使用URL来标识资源,使用HTTP功能进行缓存控制、身份验证等功能,根据这些原则设计的API称为RESTful,有常见的API定义格式,可以用于描述API并且生成文档
SOAP:基于XML协议,将许多功能由自身实现,有大量的独立标准,尽可能地独立于HTTP协议,基于SOAP实现的API被称为WSDL,通过工具生成本地代码对应的WSDL,同时WSDL较为复杂并不是为人们阅读而设计的
RPC
想像本地调用函数的方式来调用远程的方法以及代码(达到位置透明的目的),但是相比于本地调用,有着更多的不可预测性
- 当出现错误的时候,增加了网络以及服务可用性的可能性,网络带来的不确定性会使得调用方法无法完全掌握情况
- 对服务方,会增加网络服务重试带来的影响
- 网络质量的不确定性
- 网络带来的对象编码传输问题
- 高并发下的负载均衡问题
- 调用方和服务方使用的语言以及平台都可能不一样
Thrift、Protocol Buffer常用于RPC中的编码
RPC对用户的掌控能力更差了,无法强制用户进行升级,兼容性的保证工作就需要做下去了
异步消息队列
在调用方和服务放中间多加了一层代理、消息队列,相比于基于服务的数据流,调用方对调用结果的重视程度更小
异步消息队列的有点大家肯定都知道的:异步、削峰、解耦