什么是序列化
团队里系统间的数据传输与接收,主要是metaq、hsf、tair等中间件系统,经常要用到序列化与反序列化,使用的方法也是多种多样,那么,到底该如何抉择呢?
如何序列化
java
介绍
java原生序列化方式,实现了java.io.Serializable
接口的类,可以被执行串行化操作。通过serialVersionUID
来验证版本的一致性,否则抛出异常。它序列化出来的字节流是对那个对象结构到内容的完全描述,包含所有的信息,因此效率较低而且字节流比较大。
问题点
- 如果内部的字段的类没有实现java.io.Serializable,也会报错。
- 子类实现了java.io.Serializable接口,父类没有,则子类中属性能正确序列化,父类中的属性无法序列化,数据丢失。
使用
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.io.Serializable;
public class CommonUtils {
public static Serializable byteArray2Object(byte[] body) {
ByteArrayInputStream bis = null;
ObjectInputStream oi = null;
try {
bis = new ByteArrayInputStream(body);
oi = new ObjectInputStream(bis);
return (Serializable) oi.readObject();
} catch (IOException e) {
throw new RuntimeException("deserialize object failed.", e);
} catch (ClassNotFoundException e) {
throw new RuntimeException("deserialize object class not found.", e);
} finally {
closeInputStream(oi);
closeInputStream(bis);
}
}
public static byte[] object2ByteArray(Serializable obj) {
ByteArrayOutputStream bos = null;
ObjectOutputStream out = null;
try {
bos = new ByteArrayOutputStream();
out = new ObjectOutputStream(bos);
out.writeObject(obj);
out.flush();
return bos.toByteArray();
} catch (IOException e) {
throw new RuntimeException("serialize object failed.");
} finally {
closeOutputStream(out);
closeOutputStream(bos);
}
}
public static void closeOutputStream(OutputStream out) {
if (out != null) {
try {
out.close();
} catch (Exception e) {
out = null;
}
}
}
public static void closeInputStream(InputStream bis) {
if (bis != null) {
try {
bis.close();
} catch (Exception e) {
bis = null;
}
}
}
}
note:此时序列化的对象必须要实现Serializable接口,否则会报序列化异常。
public class XXXDTO implements Serializable {
private static final long serialVersionUID = -4430726442401404564L;
...
}
hessian
介绍
hessian原本是一个RPC框架,基于http传输,通过自定义的序列化协议将数据转换成二进制流,支持跨语言兼容性。由于其中序列化协议在稳定性和效率的折中上表现优异,切接入零成本,被广泛在互联网应用中使用。SerializerFactory继承AbstractSerializerFactory,而且在SerializerFactory有很多静态map用来存放类与序列化和反序列化工具类的映射,这样如果已经用过的序列化工具就可以直接拿出来用,不必再重新实例化工具类。
问题点
用hessian序列化对象是一个对象继承另外一个对象的时候,当一个属性在子类和有一个相同属性的时候,反序列化后子类属性总是为null。
如果一个对象继承于HashMap,并且增加属性,这些增加的属性不会被序列化。
使用
ByteArrayOutputStream out = outputStream(data);
Hessian2StreamingOutput hout = new Hessian2StreamingOutput(out);
hout.writeObject(data);
return out.toByteArray();
Protostuff
proto stuff是一个集成了多重协议的序列化框架。像它的名字一样,它也提供了对protobuf协议的支持。支持运行时生成schema的runtime方式,不用手写,开发接入成本低。
用法
protected static final int BUFFER_SIZE = 1024;
private static ThreadLocal<LinkedBuffer> bufferCached = new ThreadLocal<LinkedBuffer>(){
@Override
protected LinkedBuffer initialValue(){
return LinkedBuffer.allocate(BUFFER_SIZE);
}
};
@SuppressWarnings({"unchecked" })
public static byte[] serialize(Object object){
try{
return ProtostuffIOUtil.toByteArray(object, getSchema(object.getClass()), bufferCached.get());
}finally{
bufferCached.get().clear();
}
}
@SuppressWarnings({ "unchecked", "rawtypes" })
private static Schema getSchema(Class clazz) {
return RuntimeSchema.getSchema(clazz);
}
@SuppressWarnings("unchecked")
public static void deserialize(Object object, byte[] bytes){
ProtostuffIOUtil.mergeFrom(bytes, object, getSchema(object.getClass()));
}
JSON序列化
JSON的格式本身有强语义,可读。把它作为中间结果,便于问题排查。其中较快的库有alibaba的fastJson。把它用作序列化框架,其特点有:
-
中间结果可读,易于排查
-
跨平台、跨语言支持
-
速度快、但体积大
-
支持引用类型,支持循环引用。
-
由于多了层JSON的解析,引入了更多稳定应隐患,对继承、泛型的支持也不够好。
-
小結
方式 | 优点 | 缺点 | 目前使用场景 |
java原生序列化 | 稳定无bug,对Java继承体系提供完整的支持 | 性能差 非java语言无法使用 | notify、hsf可选 |
hessian | 性能好 跨语言 | 兼容性:支持引用,继承、泛型支持差 | hsf默认 |
proto stuff | 有多种协议供选择 兼容protobuf 跨语言 | 兼容性:继承、泛型支持差 | metaq |
json | 开发成本低 | 文件大 | http接口返回封装 |
简单小结下,待后续完善...