序列化理解
序列化就是将java对象转化为字节文件;反序列化就是将字节文件转化为java对象。
比如我们用到Entity都是需要实现序列化接口的。java平台允许我们在内存中创建一些可服用的对象(例如Entity),也即是在一个JVM进程中允许创建可复用对象。如果在同一个JVM进程中(可以理解为架构?),如果需要用此对象,直接传递该对象的引用就可以了。如果需要将对象保存到磁盘,需要序列化后,从内存保存到磁盘。
但是对于分布式架构,就相当于多个JVM进程在通信,这时候如果需要对象从一个进程传到另一个进程,并且可以读取出来,然后使用,这是不同的内存空间,怎么办?这时候就需要用到序列化技术,实现的手段是通过Object流,传输的时候,将对象序列化为二进制流,然后通过网络传输,接收端进程接收到序列化后的字节文件,经过反序列,转换成对象使用。
序列化技术
现在主流的序列化技术包括:JSON、Hessian、xml、protobuf、kryo、MsgPack、FST、thrift、protostuff、Avro等。
除FST、kryo默认只支持java外,其他序列化技术都是跨语言的。Java序列化机制Serialize接口也是只支持java语言。
性能对比
分布式应用系统中,系统之间的通讯质量决定了系统的可用性。在传输数据的过程中,数据包越小,时间越少,效率就越高。数据包越小,占用的宽带就越少,同等条件下资源利用就会越小。
接下来会对比jackson、FastJson、protobuf、hessian之间的性能。
1、相比json-lib框架,Jackson所依赖的jar包较少,简单易用并且性能也要相对高些。而且Jackson社区相对比较活跃,更新速度也比较快。
2、Fastjson是一个Java语言编写的高性能的JSON处理器,由阿里巴巴公司开发。无依赖,不需要例外额外的jar,能够直接跑在JDK上。
3、protobuf是谷歌出的一款开源项目,序列化文件会经过压缩,减小存储空间,从而提高传输效率,另外使用了缓冲,可以提高性能
4、hessian序列化的字节文件偏大,会占用太多存储空间。
引入四种方式用到的jar包
<!--json实现序列化-谷歌-->
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-mapper-asl</artifactId>
<version>1.9.13</version>
</dependency>
<!--fastjson实现序列化-阿里-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.51</version>
</dependency>
<!--protobuf实现序列化-谷歌-阿里提供的包jprotobuf-->
<dependency>
<groupId>com.baidu</groupId>
<artifactId>jprotobuf</artifactId>
<version>2.1.2</version>
</dependency>
<!--hessian实现序列化-->
<dependency>
<groupId>com.caucho</groupId>
<artifactId>hessian</artifactId>
<version>4.0.51</version>
</dependency>
创建Person对象
public class Person implements Serializable {
private static final long serialVersionUID = -4015446837465554319L;
private String name;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
Jackson实现序列化
public class JsonTest {
//初始化
private static Person init(){
Person person = new Person();
person.setName("Lv");
person.setAge(18);
return person;
}
public static void main(String[] args) throws IOException {
excuteWithJson();
}
/**
* Json实现序列化,验证效率和总大小
*
* TODO 结果如下:
* Json序列化:67ms:总大小:22
* Json反序列化:Person{name='Lv', age=18, height=2}
* @throws IOException
*/
private static void excuteWithJson() throws IOException {
Person person = init();
//jackson包里的序列化工具
ObjectMapper mapper = new ObjectMapper();
byte[] bytes = null;
Long start = System.currentTimeMillis();
//循环序列化100次,统计最后的总时间
for (int i= 0; i<100; i++){
bytes = mapper.writeValueAsBytes(person);
}
System.out.println("Json序列化:" + (System.currentTimeMillis() - start) + "ms:" + "总大小:" + bytes.length);
//反序列化
Person person1 = mapper.readValue(bytes, Person.class);
System.out.println("Json反序列化:" +person1);
}
}
结果如下:
FastJson实现序列化
public class JsonTest {
//初始化
private static Person init(){
Person person = new Person();
person.setName("Lv");
person.setAge(18);
return person;
}
public static void main(String[] args) throws IOException {
excuteWithJson();
excuteWithFastJson();
excuteWithHessian();
}
/**
* FastJson实现序列化,验证效率和总大小
*
* TODO 结果如下:
* FastJson序列化:132ms:总大小:22
* FastJson反序列化:Person{name='Lv', age=18, height=2}
* @throws IOException
*/
private static void excuteWithFastJson() throws IOException {
Person person = init();
String text = null;
Long start = System.currentTimeMillis();
//循环序列化100次,统计最后的总时间
for (int i= 0; i<100; i++){
text = JSON.toJSONString(person);
}
System.out.println("FastJson序列化:" + (System.currentTimeMillis() - start) + "ms:" + "总大小:" + text.getBytes().length);
//反序列化
Person person1 = JSON.parseObject(text,Person.class);
System.out.println("FastJson反序列化:" +person1);
}
结果如下:
Hessian实现序列化
public class JsonTest {
//初始化
private static Person init(){
Person person = new Person();
person.setName("Lv");
person.setAge(18);
return person;
}
public static void main(String[] args) throws IOException {
excuteWithJson();
excuteWithFastJson();
excuteWithHessian();
}
/**
* FastJson实现序列化,验证效率和总大小
*
* TODO 结果如下:
* Hessian序列化:3ms:总大小:63
* Hessian反序列化:Person{name='Lv', age=18, height=2}
* 效率最高,但是占用空间最大
* @throws IOException
*/
private static void excuteWithHessian() throws IOException {
Person person = init();
ByteArrayOutputStream os = new ByteArrayOutputStream();
HessianOutput ho = new HessianOutput(os);
Long start = System.currentTimeMillis();
//循环序列化100次,统计最后的总时间,Hessian是累加的,所以需要读取第一次的大小
for (int i= 0; i<100; i++){
ho.writeObject(person);
if(i==0){
System.out.println(os.toByteArray().length);
}
}
System.out.println("Hessian序列化:" + (System.currentTimeMillis() - start) + "ms:" + "总大小:" + os.toByteArray().length);
//反序列化
HessianInput hi = new HessianInput(new ByteArrayInputStream(os.toByteArray()));
Person person1 = (Person) hi.readObject();
System.out.println("Hessian反序列化:" +person1);
}
}
结果如下
Protobuf实现序列化
Protobuf使用比较麻烦,实体字段需要使用@Protobuf注解
@Protobuf(fieldType = FieldType.STRING)
private String name;
@Protobuf(fieldType = FieldType.INT32)
private int age;
public class ProtobufTest {
//初始化
private static Person init(){
Person person = new Person();
person.setName("Lv");
person.setAge(18);
return person;
}
public static void main(String[] args) throws IOException {
excuteWithProtobuf();
}
/**
* Protobuf实现序列化,验证效率和总大小
*
* TODO 结果如下:
* Protobuf序列化:10ms:总大小:6
* Protobuf反序列化:Person{name='Lv', age=18}
* 效率高,占用空间小
* @throws IOException
*/
private static void excuteWithProtobuf() throws IOException {
Person person = init();
Codec<Person> personCodec = ProtobufProxy.create(Person.class,false);
byte[] bytes = null;
Long start = System.currentTimeMillis();
//循环序列化100次,统计最后的总时间
for (int i= 0; i<100; i++){
bytes = personCodec.encode(person);
}
System.out.println("Protobuf序列化:" + (System.currentTimeMillis() - start) + "ms:" + "总大小:" + bytes.length);
//反序列化
Person person1 = personCodec.decode(bytes);
System.out.println("Protobuf反序列化:" +person1);
}
}
结果如下
对比
Json序列化:65ms:总大小:22
Json反序列化:Person{name='Lv', age=18, height=2}
FastJson序列化:143ms:总大小:22
FastJson反序列化:Person{name='Lv', age=18, height=2}
Hessian序列化:2ms:总大小:63
Hessian反序列化:Person{name='Lv', age=18, height=2}
Protobuf序列化:11ms:总大小:6
Protobuf反序列化:Person{name='Lv', age=18}
由以上结果可知:
文件大小:Protobuf最小,传输宽带方法占有优势;Hessian最大。
时间:Hessian和Protobuf都小。
总体来讲,json是我们常用的,也是最流行的,而Protobuf是后起之秀,性能更好,但是Protobuf的操作比较麻烦。