为什么需要序列化?
网络传输的数据必须是二进制数据,但是调用方请求的出入参数都是对象.对象不能直接在网络中传输,所以我们需要提前把它转成可传输的二进制,并且要求转化算法是可逆的,这个过程我们一般叫做"序列化".
网络传输的数据必须是二进制数据,所以在RPC调用中,对入参对象与返回值对象进行序列化与反序列化是一个必须的过程.
常用的序列化
JDK原生序列化
序列化具体的实现是 由 ObjectOutputStream 完成的,而反序列化的具体实现是由 ObjectInputStream 完成 的
序列化过程就是在读取对象数据的时候,不断加入一些特殊分隔符,这些特殊分隔符用于在 反序列化过程中截断用.
头部数据用来声明序列化协议、序列化版本,用于高低版本向后兼容
对象数据主要包括类名、签名、属性名、属性类型及属性值,当然还有开头结尾等数 据,除了属性值属于真正的对象值,其他都是为了反序列用的元数据
实际上任何一种序列化框架,核心思想就是设计一种序列化协议.
JSON
K-V方式,没有数据类型,是一种文本型序列化框架
问题:
1. JSON序列化而外空间开销大,对于大数据量服务需要巨大的内存和磁盘开销
2. JSON没有类型,JAVA强类型语言需要通过反射解决,性能差
HessianHessian是动态类型\二进制\紧凑的,并且可跨语言移植.
协议比JDK\JSON更加紧凑,性能比JDK\JSON序列化高效,生成的字节数更小
问题:
1. 官方版本对Java里面一些常见对象类型不支持,比如:
Linked 系列,LinkedHashMap、LinkedHashSet 等,但是可以通过扩展
CollectionDeserializer 类修复;
Locale 类,可以通过扩展 ContextSerializerFactory 类修复;
Byte/Short 反序列化的时候变成 Integer.
Protobuf
Google内部混合语言数据标准,是一种轻便\高效的结构化数据存储格式,可支持Java\Python\C++\Go等语言,使用前需要定义IDL(Interface description language),然后使用不同语言的IDL编译器,生成序列化工具类.
优点:
1. 序列化体积小
2. IDL能清晰地描述语义,保证应用程序之间的类型不会丢失,无需类似XML解析器
2. 序列化反序列化速度快,不需要通过反射获取类型
4. 消息格式升级和兼任性不错,可以做到向后兼容
如何选择序列化协议?
总结
服务调用的稳定性与可靠性,要比服务的性能与响应耗时更加重要。另 外对于 RPC 调用来说,整体调用上,最为耗时、最消耗性能的操作大多都是服务提供者执 行业务逻辑的操作,这时序列化的开销对于服务整体的开销来说影响相对较小.
在使用RPC框架过程中,我们构造入参\返回值对象,注意点:
1. 对象要尽量简单,没有太多的依赖关系,属性不要太多,尽量高内聚
2. 入参对象与返回值对象体积不要太大,更不要传太大的集合
3. 尽量使用简单的\常用的\开发语言原生的对象,尤其是集合类
4.对象不要有复杂的继承关系,最好不要有父子类的情况