这是我参与8月更文挑战的第18天,活动详情查看: 8月更文挑战
本文使用源码地址:simple-rpc 把对象转换为字节序列的过程称为对象的序列化。 把字节序列恢复为对象的过程称为对象的反序列化。
序列化的主要目的:
- 通过将对象序列化为字节数组,是的不共享内存通过网络连接的系统之间能够进行对象的传输。
- 通过对象序列化为字节数组,能够将对象永久存储到存储设备。
- 解决远程接口调用JVM之间内存无法共享问题。
各种常用的序列化的性能对比可以查看开源项目:jvm-serializers 。
在simple-rpc中只实现了其中一部分:
为了抽象出一个序列化/反序列化的通用服务,首先定义序列化/反序列化的通用接口。代码如下:
public interface ISerializer {
/**
* 序列化
* @param obj
* @param <T>
* @return
*/
<T> byte[] serialize(T obj);
/**
* 反序列化
* @param data
* @param clazz
* @param <T>
* @return
*/
<T> T deserialize(byte[] data, Class<T> clazz);
}
下面基于该接口,实现上述五种常用的序列化/反序列化方式。
Java默认的序列化
JDK提供了Java对象的序列化方式。Java的序列化主要通过对象输出流java.io.ObjectOutputStream
与对象输入流java.io.ObjectInputStream
来实现,其中被序列化的类需要实现java.io.Serializable
接口。代码如下:
@Slf4j
public class DefaultSerializer implements ISerializer {
@Override
public <T> byte[] serialize(T obj) {
if (obj == null) {
throw new NullPointerException("DefaultSerializer serialize data is null");
}
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
try {
ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
objectOutputStream.writeObject(obj);
objectOutputStream.close();
} catch (IOException e) {
log.error("DefaultSerializer serialize error, obj={}", obj, e);
throw new SRpcException("DefaultSerializer serialize error", e);
}
return byteArrayOutputStream.toByteArray();
}
@Override
public <T> T deserialize(byte[] data, Class<T> clazz) {
if (data == null) {
throw new NullPointerException("DefaultSerializer deserialize data is null");
}
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(data);
try {
ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream);
return (T) objectInputStream.readObject();
} catch (Exception e) {
log.error("DefaultSerializer deserialize error, data={}", data, e);
throw new SRpcException("DefaultSerializer deserialize error", e);
}
}
}
对于Java序列化有如下知识点:
- 序列化时,只对对象的状态进行保存,而不管对象的发方法。
- 当一个父类实现序列化,子类自动实现序列化,不需要显示实现
java.io.Serializable
接口。 - 当一个对象的实例变量引用其他对象,序列化改对象时也把引用对象进行序列化。
- 当某个对象被声明为
transient
,默认序列化机制会忽略该字段。
Java默认的序列化机制优缺点非常明显:
优点:
- Java语言自带,简单易用,无需引入第三方依赖。
缺点:
- 只支持Java语言,不支持跨语言。
- Java默认序列化性能欠佳,序列化后产生的码流过大,对于引用过深的对象序列化易发生内存OOM异常。
Json序列化
Json是一种轻量级的数据交换格式。Json码流较小,并且可读性好。Json常用的序列化开源工具如下:
- Jackson
- fastjson
- Gson
为了篇幅可控,我们看其中fastjson的实现方式,代码如下:
public class FastJsonSerializer implements ISerializer {
static {
ParserConfig.getGlobalInstance().addAccept("com.miracle.srpc");
}
@Override
public <T> byte[] serialize(T obj) {
if (obj == null) {
throw new NullPointerException("FastJsonSerializer serialize data is null");
}
JSON.DEFFAULT_DATE_FORMAT = "yyyy-MM-dd HH:mm:ss";
return JSON.toJSONString(obj, SerializerFeature.WriteDateUseDateFormat, SerializerFeature.WriteClassName).getBytes();
}
@Override
public <T> T deserialize(byte[] data, Class<T> clazz) {
if (data == null) {
throw new NullPointerException("FastJsonSerializer deserialize data is null");
}
return JSON.parseObject(new String(data), clazz);
}
}
Hessian序列化
Hessian是一个支持跨语言传输的二进制序列化协议。相对于Java默认的序列化机制,Hessian具有更好的性能与易用性,而且支持多种不同的语言。序列化与反序列化实现如下:
@Slf4j
public class HessianSerializer implements ISerializer {
@Override
public <T> byte[] serialize(T obj) {
if (obj == null) {
throw new NullPointerException("HessianSerializer serialize obj is null");
}
try {
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
HessianOutput output = new HessianOutput(byteArrayOutputStream);
output.writeObject(obj);
return byteArrayOutputStream.toByteArray();
} catch (IOException e) {
log.error("HessianSerializer serialize error. obj = {}", obj, e);
throw new SRpcException("HessianSerializer serialize error", e);
}
}
@Override
public <T> T deserialize(byte[] data, Class<T> clazz) {
if (data == null) {
throw new NullPointerException("HessianSerializer deserialize data is null");
}
try {
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(data);
HessianInput hessianInput = new HessianInput(byteArrayInputStream);
return (T) hessianInput.readObject();
} catch (IOException e) {
log.error("HessianSerializer deserialize error. data = {}", data, e);
throw new SRpcException("HessianSerializer deserialize error", e);
}
}
}
protobuf序列化
protobuf是Google开源的一种数据交换格式,支持多种语言,对于每一种实现都包含了相应语言的编译器和库文件。protobuf空间开销小,解析性能高,序列化后数据量少,但是它需要编写*.proto IDL文件。
使用protobuf步骤如下:
- 配置开发环境,安装Protocol Compiler代码编译器。
- 编写.proto文件,定义序列化对象的数据结构。
- 基于编写.proto文件,使用Protocol Compiler编译生成对应的序列化/反序列工具类。
- 基于生成的代码,编写自己的序列化应用。
序列化与反序列化实现如下:
@Slf4j
public class ProtoBufSerializer implements ISerializer {
@Override
public <T> byte[] serialize(T obj) {
try {
if (obj instanceof GeneratedMessageV3) {
throw new UnsupportedOperationException("ProtoBufSerializer serialize not support obj type");
}
return (byte[]) MethodUtils.invokeMethod(obj, "toByteArray");
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
log.error("ProtoBufSerializer serialize error, obj={}", obj, e);
throw new SRpcException("ProtoBufSerializer serialize error", e);
}
}
@Override
public <T> T deserialize(byte[] data, Class<T> clazz) {
try {
if (!GeneratedMessageV3.class.isAssignableFrom(clazz)) {
throw new UnsupportedOperationException("ProtoBufSerializer deserialize not support obj type");
}
return (T) MethodUtils.invokeStaticMethod(clazz, "getDefaultInstance");
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
log.error("ProtoBufSerializer deserialize error", e);
throw new SRpcException("ProtoBufSerializer deserialize error", e);
}
}
}
序列化引擎
根据不同的序列化框架,提供上述几种序列化与反序列化实现,为了序列化工具更容易使用,下面将上述的实现整合为通用的序列化工具引擎,可以通过输入不同配置随意选择使用哪种序列化方案。代码实现如下:
@Slf4j
public class SerializerEngine {
private static final Map<SerializeType, ISerializer> SERIALIZER_MAP = Maps.newHashMap();
static {
SERIALIZER_MAP.put(SerializeType.DefaultSerializer, new DefaultSerializer());
SERIALIZER_MAP.put(SerializeType.JacksonSerializer, new JacksonSerializer());
SERIALIZER_MAP.put(SerializeType.FastJsonSerializer, new FastJsonSerializer());
SERIALIZER_MAP.put(SerializeType.HessianSerializer, new HessianSerializer());
SERIALIZER_MAP.put(SerializeType.ProtoBufSerializer, new ProtoBufSerializer());
}
/**
* 序列化
* @param obj 对象
* @param serializeType 序列化方式
* @param <T>
* @return
*/
public static <T> byte[] serialize(T obj, String serializeType) {
SerializeType serialize = SerializeType.getByType(serializeType);
if (null == serialize) {
throw new SRpcException("SerializerEngine serialize error, serializeType is not support");
}
ISerializer iSerializer = SERIALIZER_MAP.get(serialize);
if (null == iSerializer) {
throw new SRpcException("SerializerEngine serialize error, get ISerializer fail");
}
try {
return iSerializer.serialize(obj);
} catch (Exception e) {
log.error("SerializerEngine serialize error, obj={}", obj, e);
throw new SRpcException("SerializerEngine serialize error");
}
}
/**
* 反序列化
* @param data 数据
* @param clazz
* @param serializeType
* @param <T>
* @return
*/
public static <T> T deserialize(byte[] data, Class<T> clazz, String serializeType) {
SerializeType serialize = SerializeType.getByType(serializeType);
if (null == serialize) {
throw new SRpcException("SerializerEngine deserialize error, serializeType is not support");
}
ISerializer iSerializer = SERIALIZER_MAP.get(serialize);
if (null == iSerializer) {
throw new SRpcException("SerializerEngine deserialize error, get ISerializer fail");
}
try {
return iSerializer.deserialize(data, clazz);
} catch (Exception e) {
log.error("SerializerEngine deserialize error, data={}", data, e);
throw new SRpcException("SerializerEngine deserialize error");
}
}
}
SerializerEngine通过静态代码块,在类加载时将序列化算法添加到本地缓存中,提供了相应的序列化与反序列方法,并且提供了可传入参数serializeType,做到序列化与反序列化的可配置化。