Java序列化与反序列化
优秀博文:IT-BLOG-CN
序列化:把对象转换为字节序列存储于磁盘或者进行网络传输的过程称为对象的序列化。
反序列化:把磁盘或网络节点上的字节序列恢复到对象的过程称为对象的反序列化。
一、序列化对象
【1】必须实现序列化接口Serializable
:Java.io.Serializable
接口。
【2】serialVersionUID
:序列化的版本号,凡是实现Serializable
接口的类都有一个静态的表示序列化版本标识符的变量。
Add default serial version ID
:生成的代码为:private static final long serialVersionUID = 1L
;
Add generated serial version ID
:生成的代码为:private static final long serialVersionUID = -5248069984631225347L
;
定义了
serialVersionUID
之后,就可以对序列化后的对象进行修改,此时不会产生新的serialVersionUID
,导致还原时出错。
【3】serialVersionUID
的取值: 此值是通过Java
运行时环境根据类的内部细节自动生成的。如果类的源代码进行了修改,再重新编译,新生成的类文件的serialVersionUID
的值也会发生变化。不同的编译器也可能会导致不同的serialVersionUID
。为了提高serialVersionUID
的独立性和确定性,建议在一个序列化类中显示的定义serialVersionUID
,为它赋予明确的值。
package com.java;import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;/*** 序列化对象* @author -zhengzx-**/
public class Player implements Serializable{/****/private static final long serialVersionUID = -5248069984631225347L;public Player(long playerId, int age, String name) {this.playerId = playerId;this.age = age;this.name = name;}private long playerId;private int age;private String name;private List<Integer> skills = new ArrayList<>();public long getPlayerId() {return playerId;}public void setPlayerId(long playerId) {this.playerId = playerId;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}public String getName() {return name;}public void setName(String name) {this.name = name;}public List<Integer> getSkills() {return skills;}public void setSkills(List<Integer> skills) {this.skills = skills;}
}
二、序列化与反序列化实例
【1】对象序列化代码如下,具体细节注释说明:Java
中通过对象流ObjectOutputStream
进行序列化。
【2】反序列化为对象,具体细节注释说明:Java
中通过对象流ObjectInputStream
进行反序列化。
package com.java;import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.Arrays;/*** Description:序列化与反序列化* @author zhengzx*/
public class JavaSerialize {public static void main(String[] args) throws Exception {Player player = new Player(10001, 21, "teacher");player.getSkills().add(10001);//序列化byte[] bytes = toBytes(player);//反序列化toPlay(bytes);}/*** Title: toBytes* Description:序列化对象* @author zhengzx* @throws Exception*/public static byte[] toBytes(Object out) throws Exception {//用于序列化后存储对象ByteArrayOutputStream byteArrayOutputStream = null;//java序列化APIObjectOutputStream objectOutputStream = null;try {byteArrayOutputStream = new ByteArrayOutputStream();objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);//将out对象进行序列化objectOutputStream.writeObject(out);//测试验证输入(获取字节数组)byte[] bs = byteArrayOutputStream.toByteArray();//将数组转化为字符串输入System.out.println(Arrays.toString(bs));return bs;} catch (IOException e) {e.printStackTrace();}finally {//关闭最外层的流(内部流会自动关闭)objectOutputStream.close();}return null;}/** Title: toPlay* Description:反序列化对象* @author zhengzx* @throws Exception*/public static void toPlay(byte[] bs) throws Exception {//创建存放二进制数据的APIByteArrayInputStream byteArrayInputStream = null;//创建反序列化对象ObjectInputStream objectInputStream = null;try {byteArrayInputStream = new ByteArrayInputStream(bs);objectInputStream = new ObjectInputStream(byteArrayInputStream);//校验测试Player player = (Player) objectInputStream.readObject();System.out.println(player.toString());} catch (IOException e) {e.printStackTrace();}finally {objectInputStream.close();}}
}
测试结果展示:
[-84, -19, 0, 5, 115, 114, 0, 15, 99, 111, 109, 46, 106, 97, 46, 80, 108, 97, 121, 101, 114]
Player [playerId=10001, age=21, name=teacher, skills=[10001]]
三、高级部分
序列化和反序列化几乎是工程师们每天都要面对的事情,但是要精确掌握这两个概念并不容易:一方面,它们往往作为框架的一部分出现而湮没在框架之中;另一方面,它们会以其他更容易理解的概念出现,例如加密、持久化。然而,序列化和反序列化的选型却是系统设计或重构一个重要的环节,在分布式、大数据量系统设计里面更为显著。恰当的序列化协议不仅可以提高系统的通用性、强健性、安全性、优化系统性能,而且会让系统更加易于调试、便于扩展。本文从多个角度去分析和讲解“序列化和反序列化”,并对比了当前流行的几种序列化协议,期望对读者做序列化选型有所帮助。
1、定义以及相关概念
互联网的产生带来了机器间通讯的需求,而互联通讯的双方需要采用约定的协议,序列化和反序列化属于通讯协议的一部分。通讯协议往往采用分层模型,不同模型每层的功能定义以及颗粒度不同,例如:TCP/IP
协议是一个四层协议,而OSI模型却是七层协议模型。在OSI
七层协议模型中展现层Presentation Layer
的主要功能是把应用层的对象转换成一段连续的二进制串,或者反过来,把二进制串转换成应用层的对象,这两个功能就是序列化和反序列化。一般而言,TCP/IP
协议的应用层对应与OSI七层协议模型的应用层,展示层和会话层,所以序列化协议属于TCP/IP
协议应用层的一部分。本文对序列化协议的讲解主要基于OSI
七层协议模型。
数据结构、对象与二进制串: 不同的计算机语言中,数据结构,对象以及二进制串的表示方式并不相同。
数据结构和对象: 对于类似Java
这种完全面向对象的语言,工程师所操作的一切都是对象Object
,来自于类的实例化。在Java
语言中最接近数据结构的概念,就是POJO(Plain Old Java Object)
或者Javabean
,那些只有setter/getter
方法的类。而在C++
这种半面向对象的语言中,数据结构和struct
对应,对象和class
对应。
二进制串: 序列化所生成的二进制串指的是存储在内存中的一块数据。C++
语言具有内存操作符,所以二进制串的概念容易理解,例如,C++
语言的字符串可以直接被传输层使用,因为其本质上就是以’\0’结尾的存储在内存中的二进制串。在Java
语言里面,二进制串的概念容易和String
混淆。实际上String
是Java
的一等公民,是一种特殊对象Object
。对于跨语言间的通讯,序列化后的数据当然不能是某种语言的特殊数据类型。二进制串在Java
里面所指的是byte[]
,byte
是Java
的8
中原生数据类型之一Primitive data types
。
2、序列化协议特性
每种序列化协议都有优点和缺点,它们在设计之初有自己独特的应用场景。在系统设计的过程中,需要考虑序列化需求的方方面面,综合对比各种序列化协议的特性,最终给出一个折衷的方案。
【1】通用性: 通用性有两个层面的意义:
第一、技术层面,序列化协议是否支持跨平台、跨语言。如果不支持,在技术层面上的通用性就大大降低了。
第二、流行程度,序列化和反序列化需要多方参与,很少人使用的协议往往意味着昂贵的学习成本;另一方面,流行度低的协议,往往缺乏稳定而成熟的跨语言、跨平台的公共包。
【2】强健性/鲁棒性: 以下两个方面的原因会导致协议不够强健:
第一、成熟度不够,一个协议从制定到实施,到最后成熟往往是一个漫长的阶段。协议的强健性依赖于大量而全面的测试,对于致力于提供高质量服务的系统,采用处于测试阶段的序列化协议会带来很高的风险。
第二、语言/平台的不公平性。为了支持跨语言、跨平台的功能,序列化协议的制定者需要做大量的工作;但是,当所支持的语言或者平台之间存在难以调和的特性的时候,协议制定者需要做一个艰难的决定–支持更多人使用的语言/平台,亦或支持更多的语言/平台而放弃某个特性。当协议的制定者决定为某种语言或平台提供更多支持的时候,对于使用者而言,协议的强健性就被牺牲了。
【3】可调试性/可读性: 序列化和反序列化的数据正确性和业务正确性的调试往往需要很长的时间,良好的调试机制会大大提高开发效率。序列化后的二进制串往往不具备人眼可读性,为了验证序列化结果的正确性,写入方不得同时撰写反序列化程序,或提供一个查询平台,这比较费时;另一方面,如果读取方未能成功实现反序列化,这将给问题查找带来了很大的挑战,难以定位是由于自身的反序列化程序的bug所导致还是由于写入方序列化后的错误数据所导致。对于跨公司间的调试,由于以下原因,问题会显得更严重:
第一、支持不到位,跨公司调试在问题出现后可能得不到及时的支持,这大大延长了调试周期。
第二、访问限制,调试阶段的查询平台未必对外公开,这增加了读取方的验证难度。
如果序列化后的数据人眼可读,这将大大提高调试效率,
XML
和JSON
就具有人眼可读的优点。
【4】性能: 性能包括两个方面,时间复杂度和空间复杂度:
第一、空间开销Verbosity
, 序列化需要在原有的数据上加上描述字段,以为反序列化解析之用。如果序列化过程引入的额外开销过高,可能会导致过大的网络,磁盘等各方面的压力。对于海量分布式存储系统,数据量往往以TB
为单位,巨大的的额外空间开销意味着高昂的成本。
第二、时间开销Complexity
,复杂的序列化协议会导致较长的解析时间,这可能会使得序列化和反序列化阶段成为整个系统的瓶颈。
【5】可扩展性/兼容性: 移动互联时代,业务系统需求的更新周期变得更快,新的需求不断涌现,而老的系统还是需要继续维护。如果序列化协议具有良好的可扩展性,支持自动增加新的业务字段,而不影响老的服务,这将大大提供系统的灵活度。
【6】安全性/访问限制: 在序列化选型的过程中,安全性的考虑往往发生在跨局域网访问的场景。当通讯发生在公司之间或者跨机房的时候,出于安全的考虑,对于跨局域网的访问往往被限制为基于HTTP/HTTPS的80和443端口。如果使用的序列化协议没有兼容而成熟的HTTP传输层框架支持,可能会导致以下三种结果之一:第一、因为访问限制而降低服务可用性。 第二、被迫重新实现安全协议而导致实施成本大大提高。 第三、开放更多的防火墙端口和协议访问,而牺牲安全性。
3、序列化和反序列化的组件
典型的序列化和反序列化过程往往需要如下组件:
【1】IDL(Interface description language)
文件: 参与通讯的各方需要对通讯的内容需要做相关的约定Specifications
。为了建立一个与语言和平台无关的约定,这个约定需要采用与具体开发语言、平台无关的语言来进行描述。这种语言被称为接口描述语言IDL
,采用 IDL
撰写的协议约定称之为IDL
文件。
【2】IDL Compiler
: IDL
文件中约定的内容为了在各语言和平台可见,需要有一个编译器,将IDL
文件转换成各语言对应的动态库。
【3】Stub/Skeleton Lib
: 负责序列化和反序列化的工作代码。Stub
是一段部署在分布式系统客户端的代码,一方面接收应用层的参数,并对其序列化后通过底层协议栈发送到服务端,另一方面接收服务端序列化后的结果数据,反序列化后交给客户端应用层;Skeleton
部署在服务端,其功能与Stub
相反,从传输层接收序列化参数,反序列化后交给服务端应用层,并将应用层的执行结果序列化后最终传送给客户端Stub
。
【4】Client/Server
: 指的是应用层程序代码,他们面对的是IDL
所生存的特定语言的class
或struct
。
【5】底层协议栈和互联网: 序列化之后的数据通过底层的传输层、网络层、链路层以及物理层协议转换成数字信号在互联网中传递。
序列化组件与数据库访问组件的对比: 数据库访问对于很多工程师来说相对熟悉,所用到的组件也相对容易理解。下表类比了序列化过程中用到的部分组件和数据库访问组件的对应关系,以便于大家更好的把握序列化相关组件的概念。
序列化组件 | 数据库组件 | 说明 |
---|---|---|
IDL | DDL | 用于建表或者模型的语言 |
DL file | DB Schema | 表创建文件或模型文件 |
Stub/Skeleton lib | O/R mapping | 将class和Table或者数据模型进行映射 |
4、几种常见的序列化和反序列化协议
这里主要介绍和对比几种当下比较流行的序列化协议,包括
XML
、JSON
、Protobuf
、Thrift
和Avro
。
序列化和反序列化的出现往往晦涩而隐蔽,与其他概念之间往往相互包容。为了更好的理解序列化和反序列化的相关概念在每种协议里面的具体实现,我们将一个例子穿插在各种序列化协议讲解中。在该例子中,我们希望将一个用户信息在多个系统里面进行传递;在应用层,如果采用Java语言,所面对的类对象如下所示:
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&SOAP
XML
是一种常用的序列化和反序列化协议,具有跨机器,跨语言等优点。 XML
历史悠久,其1.0
版本早在1998
年就形成标准,并被广泛使用至今。XML的最初产生目标是对互联网文档Document
进行标记,所以它的设计理念中就包含了对于人和机器都具备可读性。 但是,当这种标记文档的设计被用来序列化对象的时候,就显得冗长而复杂Verbose and Complex
。XML
本质上是一种描述语言,并且具有自我描述Self-describing
的属性,所以XML
自身就被用于XML
序列化的IDL
。标准的XML
描述格式有两种:DTD(Document Type Definition)
和XSD(XML Schema Definition)
。作为一种人眼可读Human-readable
的描述语言,XML
被广泛使用在配置文件中,例如O/R mapping
、Spring Bean Configuration File
等。
SOAP(Simple Object Access protocol)
是一种被广泛应用的,基于XML
为序列化和反序列化协议的结构化消息传递协议。SOAP
在互联网影响如此大,以至于我们给基于SOAP
的解决方案一个特定的名称Web service
。SOAP
虽然可以支持多种传输层协议,不过SOAP
最常见的使用方式还是XML+HTTP
。SOAP协议的主要接口描述语言IDL
是WSDL(Web Service Description Language)
。SOAP
具有安全、可扩展、跨语言、跨平台并支持多种传输层协议。如果不考虑跨平台和跨语言的需求,XML的在某些语言里面具有非常简单易用的序列化使用方法,无需IDL
文件和第三方编译器, 例如Java+XStream
。
SOAP
是一种采用XML进行序列化和反序列化的协议,它的IDL
是WSDL
. 而WSDL
的描述文件是XSD
,而XSD
自身是一种XML
文件。 这里产生了一种有趣的在数学上称之为“递归”的问题,这种现象往往发生在一些具有自我属性Self-description
的事物上。
IDL
文件举例,采用WSDL
描述上述用户基本信息的例子如下:
<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>
典型应用场景和非应用场景: SOAP
协议具有广泛的群众基础,基于HTTP
的传输协议使得其在穿越防火墙时具有良好安全特性,XML
所具有的人眼可读Human-readable
特性使得其具有出众的可调试性,互联网带宽的日益剧增也大大弥补了其空间开销大Verbose
的缺点。对于在公司之间传输数据量相对小或者实时性要求相对低(例如秒级别)的服务是一个好的选择。
由于XML
的额外空间开销大,序列化之后的数据量剧增,对于数据量巨大序列持久化应用常景,这意味着巨大的内存和磁盘开销,不太适合XML
。另外,XML
的序列化和反序列化的空间和时间开销都比较大,对于对性能要求在 ms级别的服务,不推荐使用。WSDL
虽然具备了描述对象的能力,SOAP
的S
代表的也是simple
,但是SOAP
的使用绝对不简单。对于习惯于面向对象编程的用户,WSDL
文件不直观。
JSON(Javascript Object Notation)
JSON
起源于弱类型语言Javascript
,它的产生来自于一种称之为Associative array
的概念,其本质是就是采用Attribute-value
的方式来描述对象。实际上在Javascript
和PHP
等弱类型语言中,类的描述方式就是Associative array
。JSON
的如下优点,使得它快速成为最广泛使用的序列化协议之一:
【1】这种Associative array
格式非常符合工程师对对象的理解。
【2】它保持了XML
的人眼可读Human-readable
的优点。
【3】相对于XML
而言,序列化后的数据更加简洁。 来自于的以下链接的研究表明:XML
所产生序列化之后文件的大小接近JSON
的两倍。
【4】它具备Javascript
的先天性支持,所以被广泛应用于Web browser
的应用常景中,是Ajax
的事实标准协议。
【5】与XML
相比,其协议比较简单,解析速度比较快。
【6】松散的Associative array
使得其具有良好的可扩展性和兼容性。
IDL
悖论:JSON
实在是太简单了,或者说太像各种语言里面的类了,所以采用JSON
进行序列化不需要IDL
。这实在是太神奇了,存在一种天然的序列化协议,自身就实现了跨语言和跨平台。然而事实没有那么神奇,之所以产生这种假象,来自于两个原因:
【1】Associative array在弱类型语言里面就是类的概念,在 PHP和 Javascript里面 Associative array就是其 class的实际实现方式,所以在这些弱类型语言里面,JSON得到了非常良好的支持。
【2】IDL
的目的是撰写IDL
文件,而IDL
文件被IDL Compiler
编译后能够产生一些代码Stub/Skeleton
,而这些代码是真正负责相应的序列化和反序列化工作的组件。但是由于Associative array
和一般语言里面的class
太像了,他们之间形成了一一对应关系,这就使得我们可以采用一套标准的代码进行相应的转化。对于自身支持Associative array
的弱类型语言,语言自身就具备操作JSON
序列化后的数据的能力;对于Java
这强类型语言,可以采用反射的方式统一解决,例如Google
提供的Gson
。
典型应用场景和非应用场景:JSON
在很多应用场景中可以替代XML
,更简洁并且解析速度更快。典型应用场景包括:
【1】公司之间传输数据量相对小,实时性要求相对低(例如秒级别)的服务。
【2】基于Web browser
的Ajax
请求。
【3】由于JSON
具有非常强的前后兼容性,对于接口经常发生变化,并对可调式性要求高的场景,例如Mobile app
与服务端的通讯。
【4】由于JSON
的典型应用场景是JSON+HTTP
,适合跨防火墙访问。
总的来说,采用JSON
进行序列化的额外空间开销比较大,对于大数据量服务或持久化,这意味着巨大的内存和磁盘开销,这种场景不适合。没有统一可用的IDL
降低了对参与方的约束,实际操作中往往只能采用文档方式来进行约定,这可能会给调试带来一些不便,延长开发周期。 由于JSON
在一些语言中的序列化和反序列化需要采用反射机制,所以在性能要求为ms
级别,不建议使用。
IDL
文件举例:以下是UserInfo
序列化之后的一个例子:
{"userid":1,"name":"messi","address":[{"city":"北京","postcode":"1000000","street":"wangjingdonglu"}]}
Thrift
Thrift
是Facebook
开源提供的一个高性能,轻量级RPC
服务框架,其产生正是为了满足当前大数据量、分布式、跨语言、跨平台数据通讯的需求。 但是,Thrift
并不仅仅是序列化协议,而是一个RPC
框架。相对于JSON
和XML
而言,Thrift
在空间开销和解析性能上有了比较大的提升,对于对性能要求比较高的分布式系统,它是一个优秀的RPC
解决方案;但是由于Thrift
的序列化被嵌入到Thrift
框架里面,Thrift
框架本身并没有透出序列化和反序列化接口,这导致其很难和其他传输层协议共同使用(例如HTTP
)。
典型应用场景和非应用场景:对于需求为高性能,分布式的RPC
服务,Thrift
是一个优秀的解决方案。它支持众多语言和丰富的数据类型,并对于数据字段的增删具有较强的兼容性。所以非常适用于作为公司内部的面向服务构建SOA
的标准 RPC框架。不过Thrift
的文档相对比较缺乏,目前使用的群众基础相对较少。另外由于其Server
是基于自身的Socket
服务,所以在跨防火墙访问时,安全是一个顾虑,所以在公司间进行通讯时需要谨慎。 另外Thrift
序列化之后的数据是Binary
数组,不具有可读性,调试代码时相对困难。最后,由于Thrift
的序列化和框架紧耦合,无法支持向持久层直接读写数据,所以不适合做数据持久化序列化协议。
IDL
文件举例:
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
Protobuf
具备了优秀的序列化协议的所需的众多典型特征:
【1】标准的IDL
和IDL
编译器,这使得其对工程师非常友好。
【2】序列化数据非常简洁,紧凑,与XML
相比,其序列化之后的数据量约为1/3
到1/10
。
【3】解析速度非常快,比对应的XML
快约20-100倍。
【4】提供了非常友好的动态库,使用非常简介,反序列化只需要一行代码。
Protobuf
是一个纯粹的展示层协议,可以和各种传输层协议一起使用;Protobuf
的文档也非常完善。 但是由于Protobuf
产生于Google
,所以目前其仅仅支持Java
、C++
、Python
三种语言。另外Protobuf
支持的数据类型相对较少,不支持常量类型。由于其设计的理念是纯粹的展现层协议Presentation Layer
,目前并没有一个专门支持Protobuf
的RPC
框架。
典型应用场景和非应用场景:Protobuf
具有广泛的用户基础,空间开销小以及高解析性能是其亮点,非常适合于公司内部的对性能要求高的RPC
调用。由于Protobuf
提供了标准的IDL
以及对应的编译器,其IDL
文件是参与各方的非常强的业务约束,另外,Protobuf
与传输层无关,采用HTTP
具有良好的跨防火墙的访问属性,所以Protobuf
也适用于公司间对性能要求比较高的场景。由于其解析性能高,序列化后数据量相对少,非常适合应用层对象的持久化场景。
它的主要问题在于其所支持的语言相对较少,另外由于没有绑定的标准底层传输层协议,在公司间进行传输层协议的调试工作相对麻烦。
IDL
文件举例
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
Avro
的产生解决了JSON
的冗长和没有IDL
的问题,Avro
属于Apache Hadoop
的一个子项目。Avro
提供两种序列化格式:JSON
格式或者Binary
格式。Binary
格式在空间开销和解析性能方面可以和Protobuf
媲美,JSON
格式方便测试阶段的调试。Avro
支持的数据类型非常丰富,包括 C++语言里面的 union
类型。Avro
支持JSON格式的IDL
和类似于Thrift
和Protobuf
的IDL
(实验阶段),这两者之间可以互转。Schema
可以在传输数据的同时发送,加上JSON
的自我描述属性,这使得Avro
非常适合动态类型语言。Avro
在做文件持久化的时候,一般会和Schema
一起存储,所以Avro
序列化文件自身具有自我描述属性,所以非常适合于做Hive
、Pig
和MapReduce
的持久化数据格式。对于不同版本的Schema
,在进行RPC
调用的时候,服务端和客户端可以在握手阶段对Schema
进行互相确认,大大提高了最终的数据解析速度。
典型应用场景和非应用场景:Avro
解析性能高并且序列化之后的数据非常简洁,比较适合于高性能的序列化服务。
由于
Avro
目前非JSON
格式的IDL
处于实验阶段,而JSON
格式的IDL
对于习惯于静态类型语言的工程师来说不直观。
IDL
文件举例
protocol Userservice {record Address {string city;string postcode;string street;}record UserInfo {string name;int userid;array<Address> address = [];}
}
essess
所对应的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" : { }
}
5、Benchmark
以及选型建议
解析性能
序列化之空间开销
从上图可得出如下结论:
【1】XML
序列化Xstream
无论在性能和简洁性上比较差。
【2】Thrift
与Protobuf
相比在时空开销方面都有一定的劣势。
【3】Protobuf
和Avro
在两方面表现都非常优越。
选型建议: 以上描述的五种序列化和反序列化协议都各自具有相应的特点,适用于不同的场景:
【1】对于公司间的系统调用,如果性能要求在100ms以上的服务,基于XML
的SOAP
协议是一个值得考虑的方案。
【2】基于Web browser
的Ajax
,以及Mobile app
与服务端之间的通讯,JSON
协议是首选。对于性能要求不太高,或者以动态类型语言为主,或者传输数据载荷很小的的运用场景,JSON
也是非常不错的选择。
【3】对于调试环境比较恶劣的场景,采用JSON
或XML
能够极大的提高调试效率,降低系统开发成本。
【4】当对性能和简洁性有极高要求的场景,Protobuf
,Thrift
,Avro
之间具有一定的竞争关系。
【5】对于T
级别的数据的持久化应用场景,Protobuf
和Avro
是首要选择。如果持久化后的数据存储在Hadoop
子项目里,Avro
会是更好的选择。
【6】由于Avro
的设计理念偏向于动态类型语言,对于动态语言为主的应用场景,Avro
是更好的选择。
【7】对于持久层非Hadoop
项目,以静态类型语言为主的应用场景,Protobuf
会更符合静态类型语言工程师的开发习惯。
【8】如果需要提供一个完整的RPC
解决方案,Thrift
是一个好的选择。
【9】如果序列化之后需要支持不同的传输层协议,或者需要跨防火墙访问的高性能场景,Protobuf
可以优先考虑。
相关文章:

Java序列化与反序列化
优秀博文:IT-BLOG-CN 序列化:把对象转换为字节序列存储于磁盘或者进行网络传输的过程称为对象的序列化。 反序列化:把磁盘或网络节点上的字节序列恢复到对象的过程称为对象的反序列化。 一、序列化对象 【1】必须实现序列化接口Serializabl…...

【网络】网络层协议——IP
目录网络层IP协议IP基础知识IP地址IP报头格式网段划分CIDR特殊的IP地址IP地址的数量限制私有IP地址和公有IP地址路由IP总结网络层 在复杂的网络环境中确定一个合法的路径。 IP协议 IP协议作为整个TCP/IP中至关重要的协议,主要负责将数据包发送给最终的目标计算机…...

安装kubernetes
master110.10.10.10docker、kubelet、kubeadm、kubectlmaster210.10.10.11docker、kubelet、kubeadm、kubectlnode110.10.10.12docker、kubelet、kubeadm、kubectlnode210.10.10.13docker、kubelet、kubeadm、kubectl 1.关闭防火墙(所有节点执行) syste…...

三维点云转深度图
文章目录 目录 一、算法原理 算法流程 二、代码实现 1.Python代码 2....

Qt音视频开发27-ffmpeg视频旋转显示
一、前言 用手机或者平板拍摄的视频文件,很可能是旋转的,比如分辨率是1280x720,确是垂直的,相当于分辨率变成了720x1280,如果不做旋转处理的话,那脑袋必须歪着看才行,这样看起来太难受…...

python例程:《彩图版飞机大战》程序
目录开发环境要求运行方法《彩图版飞机大战》程序使用说明源码示例源码及说明文档下载路径开发环境要求 本系统的软件开发及运行环境具体如下。 操作系统:Windows 7、Windows 10。 Python版本:Python 3.7.1。 开发工具:PyCharm 2018。…...

【前端八股文】JavaScript系列:Set、Map、String常用属性方法
文章目录Set概念与arr的比较属性和方法并集、交集、差集Map概念属性和方法String用索引值和charAt()的区别charAt()和charCodeAt()方法的区别5个查找方法的区别如何把字符串分割为数组3个截取方法的区别大小写转换3个模式匹配方法(正则表达式)3个移除字符…...

跳跃-动态规划问题
跳跃-动态规划问题1、题目描述2、解题思路2.1 解法一:动态规划2.2 解法二:DFS深度优先搜索最大权值1、题目描述 小蓝在一个 n 行 m 列的方格图中玩一个游戏。 开始时,小蓝站在方格图的左上角,即第 11 行第 11 列。 小蓝可以在方格…...

Django笔记三十九之settings配置介绍
这一篇笔记介绍 Django 里 settings.py 里一些常用的配置项,这些配置有一些是在之前的笔记中有过介绍的,比如 logging 的日志配置,session 的会话配置等,这里就只做一下简单的回顾,有一些是之前没有介绍过的就着重介绍…...

【JavaSE】类和对象(中)
类和对象(中)4. this引用4.1 为什么要有this引用4.2 什么是this引用4.3 this引用的特性5. 对象的构造及初始化5.1 如何初始化对象5.2 构造方法(构造器)5.2.1 概念5.2.2 特性5.3 默认初始化5.4 就地初始化6. 封装6.1 封装的概念6.2…...

C语言例程:学生成绩管理程序
学生成绩管理程序 实例说明 编制一个统计存储在文件中的学生考试分数的管理程序。设学生成绩以一个学生一条记录的 形式存储在文件中,每个学生记录包含的信息有姓名、学号和各门功课的成绩。要求编制具有以 下几项功能的程序:求出各门课程的总分&#…...

完美日记母公司再度携手中国妇基会,以“创美人生”助力女性成长
撰稿 | 多客 来源 | 贝多财经 当春时节,梦想花开。和煦的三月暖阳,唤醒的不止是满城春意,更有逸仙电商“创美人生”公益项目播撒的一份希望。 3月8日“国际妇女节”当日,为积极响应我国促进共同富裕的政策倡导,助力相…...

【JaveEE】线程的创建及常见方法解析(Tread类)
目录 1.Tread类介绍 2线程的构造方法——创建线程 1.继承Thread类,重写run()方法 2.使用Runnbable接口创建线程 3.继承 Thread, 重写 run, 使用匿名内部类 4.实现 Runnable, 重写 run, 使用匿名内部类 5.使用 lambda 表达式(重点掌握)…...

Linux的诞生过程
个人简介:云计算网络运维专业人员,了解运维知识,掌握TCP/IP协议,每天分享网络运维知识与技能。座右铭:海不辞水,故能成其大;山不辞石,故能成其高。个人主页:小李会科技的…...

面部表情识别1:表情识别数据集(含下载链接)
面部表情识别1:表情识别数据集(含下载链接) 目录 面部表情识别1:表情识别数据集(含下载链接) 1.前言 2.表情识别数据集介绍 1.JAFFE数据集 2.KDEF(Karolinska Directed Emotional Faces)数据集 3.GENKI数据集 4.RaFD数据集…...

CSS实现文字凹凸效果
使用两个div分别用来实现凹凸效果;text-shadow语法 text-shadow: h-shadow v-shadow blur color; h-shadow:必需。水平阴影的位置。允许负值。 v-shadow :必需。垂直阴影的位置。允许负值。 blur:可选,模糊的距离。 co…...

嵌入式常使用的库函数
自己创建简单的mcu中常用的库函数 文章目录自己创建简单的mcu中常用的库函数1. 自己编写库函数的意义2. 计算字符串长度.以\0作为结束符3. 复制字符串4. 字符串比较5. 将整数转换为ASCII数组6. 将ASCII码字符串转换成整数7. 将字节数组转换为16位整数8.计算CRC,用于Modbus协议9…...

【业务安全-02】业务逻辑漏洞之越权操作
越权越权即越权查看被人的信息,又分为水平越权和垂直越权,但是两者的本质都是一样的,只是越权的身份权限不一样而已水平越权:相同级别的用户,如用户A访问用户B垂直越权:普通用户到管理员,普通用…...

完全小白的pycharm深度学习调试+for循环断点条件设置
完全小白的pycharm深度学习调试for循环断点条件设置写在最前面基础方法pycharm断点调试控制台输入代码中循环的debug方法pycharm中图标的介绍常见的BugDebug经验1. 检查激活函数的输入值2. 检查梯度3. 消融实验4. 使用最短的时间5. 静下心来写在最前面 之前把seq2seqattention…...

直方图及其应用
直方图定义直方图是一种描述数据的分布通过将连续变量划分成一系列区间,统计区间频率,并用来表示,以表征其统计特征在图像处理中,直方图可以用来表示图像中像素值的分布状况,描述不同灰度级的像素在图像中的占比直方图…...

《SpringBoot篇》26.SpringBoot整合Jackson超详细教程(附Jackson工具类)
陈老老老板🦸👨💻本文专栏:SpringBoot篇(主要讲一些与springboot整合相关的内容)👨💻本文简述:本文讲一下Jackson常见用法,超级详细。👨&am…...

Redis 如何实现库存扣减操作和防止被超卖?
本文已经收录到Github仓库,该仓库包含计算机基础、Java基础、多线程、JVM、数据库、Redis、Spring、Mybatis、SpringMVC、SpringBoot、分布式、微服务、设计模式、架构、校招社招分享等核心知识点,欢迎star~ Github地址:https://github.com/…...

(Linux)Ubuntu查看系统版本
uname -a : 查看操作系统的发行版号和操作系统版本 Command: uname -aResult: Linux SERVER 5.19.0-35-generic #36-Ubuntu SMP PREEMPT_DYNAMIC Fri Feb 3 18:36:56 UTC 2023 x86_64 x86_64 x86_64 GNU/Linux uname -v : 查看版本号 Command: uname -vResult: #36-Ubuntu …...

VxWorkds 内存管理(3)
虚拟内存管理 对于带MMU的目标板,VxWorks提供虚拟内存的支持,VxWorks提供了两种虚拟内存管理单元(MMU)的支持: 基本MMU和VxVMI 基本MMU邦定于VxWorks中,可以通过config.h中宏定义INCLUDE MMU BASIC或Tornado工程配置中包含基本MMU组件 VxV…...

单元测试、反射、注解、动态代理
🏡个人主页 : 守夜人st 🚀系列专栏:Java …持续更新中敬请关注… 🙉博主简介:软件工程专业,在校学生,写博客是为了总结回顾一些所学知识点 目录单元测试、反射、注解、动态代理单元测…...

【数据结构】夯实基础|线性表刷题01
作者:努力学习的大一在校计算机专业学生,热爱学习和创作。目前在学习和分享:算法、数据结构、Java等相关知识。博主主页: 是瑶瑶子啦所属专栏: 【数据结构|刷题专栏】:该专栏专注于数据结构知识,持续更新&a…...

Java怎么实现几十万条数据插入(30万条数据插入MySQL仅需13秒)
本文主要讲述通过MyBatis、JDBC等做大数据量数据插入的案例和结果。 30万条数据插入插入数据库验证实体类、mapper和配置文件定义User实体mapper接口mapper.xml文件jdbc.propertiessqlMapConfig.xml不分批次直接梭哈循环逐条插入MyBatis实现插入30万条数据JDBC实现插入30万条数…...

java多线程之线程的六种状态
线程的六种状态(1) NEW(初始状态)(2) TERMINATED(终止状态 / 死亡状态)(3) RUNNABLE(运行时状态)(4) TIMED_WAITING(超时等待状态)(5) WAITING(等待状态)(6) BLOCK(阻塞状态)sleep和wait的区别:操作系统里的线程自身是有一个状态的,但是java Thread 是对系统线程的封装,把这里的…...

UnixBench----x86架构openEuler操作系统上进行性能测试
【原文链接】UnixBench----x86架构openEuler操作系统上进行性能测试 (1)打开github上 UnixBench 地址,找到发布的tag (2)找到tar.gz包,右键复制链接 比如这里是 https://github.com/kdlucas/byte-unix…...

于Java8 Stream教程之collect()
目录 前言正文第一个小玩法 将集合通过Stream.collect() 转换成其他集合/数组:第二个小玩法 聚合(求和、最小、最大、平均值、分组)总结前言 本身我是一个比较偏向少使用Stream的人,因为调试比较不方便。 但是, 不得不说&#…...