一、使用场景
1、网络传输
2、加密、持久化
二、概念
1、通信协议中的位置
七层模型的表示层或者四层模型的应用层
2、概念
序列化 : 数据结构或者对象 -> 二进制
反序列化 : 二进制 -> 数据结构或者对象
注:二进制串 存储在内存中的一块数据, C++的字符串、 Java的byte[]数组
三、参考指标
每种序列化协议都有优点和缺点,它们在设计之初有自己独特的应用场景。在系统设计的过程中,需要考虑序列化需求的方方面面,综合对比各种序列化协议的特性,最终给出一个折衷的方案。
1、通用性
(1) 是否跨平台, 跨语言
(2) 协议是否成熟, 是否流行, 使用人数多少
2、可读性/可调试性
(1) 提高开发效率
(2) 便于排查问题(支持不到位、访问限制)
3、性能
(1) 空间开销
(2) 时间开销
4、可扩展性/兼容性
具有良好的可扩展性, 支持自动增加新的业务字段, 而不影响老的服务, 这将大大提供系统的灵活度
5、安全性/访问控制
协议与端口限制 : http&https协议、80&443端口
(1) 因访问限制而降低服务可用性
(2) 重新实现安全协议而导致提高实施成本
(3) 开放更多的防火墙端口和协议访问, 降低安全性
四、序列化与反序列化协议
通用组件
IDL(Interface description language)文件 : 参与通讯的各方需要对通讯的内容需要做相关的约定, 该约定于语言无关, 与平台无关
IDL Complier : IDL文件中约定的内容为了在各语言和平台可见,需要有一个编译器,将IDL文件转换成各语言对应的动态库
Client/Server : 指的是应用层程序代码,他们面对的是IDL所生存的特定语言的class或struct
Stub/Skeleton Lib : 负责序列化和反序列化的工作代码
底层协议栈和互联网 : 序列化之后的数据通过底层的传输层、网络层、链路层以及物理层协议转换成数字信号在互联网中传递
流程图
待序列化类
class Address {
private String city;
private String postcode;
private String street;
}
public class UserInfo {
private Integer userid;
private String name;
private List<Address> address;
}
XML
(1) 基于http协议可穿透防火墙, 可读性高
(2) 空间开销大, 时间开销大
(3) 适合实时性要求较低(例如秒级)的服务
<xsd:complexType name='Address'>
<xsd:attribute name='city' type='xsd:string' />
<xsd:attribute name='postcode' type='xsd:string' />
<xsd:attribute name='street' type='xsd:string' />
</xsd:complexType>
<xsd:complexType name='UserInfo'>
<xsd:sequence>
<xsd:element name='address' type='tns:Address'/>
<xsd:element name='address1' type='tns:Address'/>
</xsd:sequence>
<xsd:attribute name='userid' type='xsd:int' />
<xsd:attribute name='name' type='xsd:string' />
</xsd:complexType>
### JSON
(1) 基于http协议可穿透防火墙, 可读性高, 空间开销比XML(1/2)小, 解析时间开销比XML小, 基于关联数组的实现有更好的可扩展性和兼容性
(2) 一定的空间消耗, 不适合持久化存储; 没有统一的IDL约束, 给调试带来麻烦; 在一些语言中需要反射来实现序列化和反序列化, 增加时间消耗。
(3) 适合实时性要求较低(如秒级), 数据量传输较少的服务; 基于web browser的ajax请求; 接口经常变动, 可调试性要求较高的服务。
{"userid":1,"name":"messi","address":[{"city":"北京","postcode":"1000000","street":"wangjingdonglu"}]}
Thrift
(1) 支持多种语言; 支持RPC调用
(2) Thrift的序列化被嵌入到Thrift框架里面, 很难和其他传输层协议共同使用(Http); 文档缺乏, 学习成本高; 数据格式为二进制, 可读性差; 序列化与框架紧耦合, 无法实现持久化功能
(3) 适合实时性要求较高的大数据量、分布式、跨平台、跨语言的服务
struct Address {
1: required string city;
2: optional string postcode;
3: optional string street;
}
struct UserInfo {
1: required string userid;
2: required i32 name;
3: optional list<Address> address;
}
Protobuf
(1) IDL和IDL编译器对工程师友好; 空间开销小(约为XML的1/3到1/10); 时间开销小(约为XML的20到100倍); 纯粹的序列化协议, 可与各种传输层协议结合使用; 文档非常完善
(2) 支持的数据类型相对较少; 不支持RPC框架
(3) 适合公司内实时性要求较高的服务; 可与http协议结合, 穿透防火墙, 适合公司间对性能要求较高的场景; 解析性能高, 序列化数据量少, 适合持久化
message Address{
required string city=1;
optional string postcode=2;
optional string street=3;
}
message UserInfo{
required string userid=1;
required string name=2;
repeated Address address=3;
}
Avro
(1) 两种序列化格式JSON和二进制; 二进制的空间开销和时间开销媲美Protobuf; 支持数据类型丰富; 支持JSON格式的IDL和类似Thrift和Protobuf的IDL; 持久化连同Schema一起存储, 对Hive Pig Hadoop具有亲和力; 具有向前兼容与向后兼容的特性; IDL Schema支持动态加载, 支持程序中动态编译Schema
(2) JSON格式的IDL对于习惯于静态类型语言的工程师来说不直观
(3) 适合公司内实时性要求较高的服务; 可与http协议结合, 穿透防火墙, 适合公司间对性能要求较高的场景; 解析性能高, 序列化数据量少, 适合持久化
IDL文件:
protocol Userservice {
record Address {
string city;
string postcode;
string street;
}
record UserInfo {
string name;
int userid;
array<Address> address = [];
}
}
IDL JSON Schema 文件:
{
"protocol" : "Userservice",
"namespace" : "org.apache.avro.ipc.specific",
"version" : "1.0.5",
"types" : [ {
"type" : "record",
"name" : "Address",
"fields" : [ {
"name" : "city",
"type" : "string"
}, {
"name" : "postcode",
"type" : "string"
}, {
"name" : "street",
"type" : "string"
} ]
}, {
"type" : "record",
"name" : "UserInfo",
"fields" : [ {
"name" : "name",
"type" : "string"
}, {
"name" : "userid",
"type" : "int"
}, {
"name" : "address",
"type" : {
"type" : "array",
"items" : "Address"
},
"default" : [ ]
} ]
} ],
"messages" : { }
}