说到序列化,这在RPC的层面上也是很重要的一个环节,因为在我们的业务层面,你传输的一个对象,是一个Object,不过在网络上,却不管你传输的是Obj1,还是Obj2,网络只认byte,所以在代码层面上,如何将对象转化成byte数组,和如何将byte数组反序列化层对象,这也是很重要的,直接影响你的整个框架的性能
java原生态的java.io.Serializable就对序列化提供了支持,不过性能和序列化的结果并不是很让人满意的,在众多的序列化工具中,例如protostuff。kryo,fastjson等等,N多序列化的工具,估计java原生的是比较差的(如果不差,这些第三方的序列化的工具也不会出现了)
关于这些序列化的工具的性能对比我们不做研究,有兴趣的可以查看:
https://github.com/eishay/jvm-serializers/wiki
有人说,不支持多种序列化方式的RPC框架,不是好的RPC框架,所以我们还是实现几个吧,我们基于SPI的方式(SPI 简介http://www.cnblogs.com/softlin/p/4321955.html)去实现protostuff,fastjson,kryo这三种序列化方式
(注:学习RPC之前,学些Jupiter之前,我也不会SPI,序列化,但是学习之后就算入门了,以后看到了也能看明白,至少能说出一二,抛开RPC不谈,这也是积累吧)
废话不多说,我们先上代码
1)先定义序列化和反序列化的接口
package org.laopopo.common.serialization;
/**
*
* @author BazingaLyn
* @description 序列化接口
* @time 2016年8月12日
* @modifytime
*/
public interface Serializer {
/**
* 将对象序列化成byte[]
* @param obj
* @return
*/
<T> byte[] writeObject(T obj);
/**
* 将byte数组反序列成对象
* @param bytes
* @param clazz
* @return
*/
<T> T readObject(byte[] bytes, Class<T> clazz);
}
2)写一个基于SPI的调用入口方法
SerializerHolder.java
package org.laopopo.common.serialization;
import org.laopopo.common.spi.BaseServiceLoader;
/**
*
* @author BazingaLyn
* @description 序列化的入口,基于SPI方式
* @time 2016年8月12日
* @modifytime
*/
public final class SerializerHolder {
// SPI
private static final Serializer serializer = BaseServiceLoader.load(Serializer.class);
public static Serializer serializerImpl() {
return serializer;
}
}
BaseServiceLoader.java
package org.laopopo.common.spi;
import java.util.ServiceLoader;
/**
*
* @author BazingaLyn
* @description SPI loader
* @time 2016年8月11日
* @modifytime
*/
public final class BaseServiceLoader {
public static <S> S load(Class<S> serviceClass) {
return ServiceLoader.load(serviceClass).iterator().next();
}
}
因为了解SPI之后,我们还知道我们需要创建一个文本文件:
Serializer中的内容我们默认使用protostuff实现序列化:
org.laopopo.common.serialization.proto.ProtoStuffSerializer
3)具体实现
①基于ProtoStuff实现:
maven依赖
<protostuff.version>1.3.5</protostuff.version>
<objenesis.version>2.1</objenesis.version>
<dependency>
<groupId>io.protostuff</groupId>
<artifactId>protostuff-runtime</artifactId>
<version>${protostuff.version}</version>
</dependency>
<dependency>
<groupId>org.objenesis</groupId>
<artifactId>objenesis</artifactId>
<version>${objenesis.version}</version>
</dependency>
具体实现类ProtoStuffSerializer.java
package org.laopopo.common.serialization.proto;
import io.protostuff.LinkedBuffer;
import io.protostuff.ProtostuffIOUtil;
import io.protostuff.Schema;
import io.protostuff.runtime.RuntimeSchema;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.laopopo.common.serialization.Serializer;
import org.objenesis.Objenesis;
import org.objenesis.ObjenesisStd;
/**
*
* @author BazingaLyn
* @description 使用protoStuff序列化
* 序列化的对象不需要实现java.io.Serializable 也不需要有默认的构造函数
* @time 2016年8月12日
* @modifytime
*/
public class ProtoStuffSerializer implements Serializer {
private static Map<Class<?>, Schema<?>> cachedSchema = new ConcurrentHashMap<Class<?>, Schema<?>>();
private static Objenesis objenesis = new ObjenesisStd(true);
@SuppressWarnings("unchecked")
public <T> byte[] writeObject(T obj) {
System.out.println("ProtoStuffSerializer Serializer");
Class<T> cls = (Class<T>) obj.getClass();
LinkedBuffer buffer = LinkedBuffer.allocate(LinkedBuffer.DEFAULT_BUFFER_SIZE);
try {
Schema<T> schema = getSchema(cls);
return ProtostuffIOUtil.toByteArray(obj, schema, buffer);
} catch (Exception e) {
throw new IllegalStateException(e.getMessage(), e);
} finally {
buffer.clear();
}
}
public <T> T readObject(byte[] bytes, Class<T> clazz) {
try {
System.out.println("ProtoStuffSerializer Deserializer");
T message = objenesis.newInstance(clazz);
Schema<T> schema = getSchema(clazz);
ProtostuffIOUtil.mergeFrom(bytes, message, schema);
return message;
} catch (Exception e) {
throw new IllegalStateException(e.getMessage(), e);
}
}
@SuppressWarnings("unchecked")
private static <T> Schema<T> getSchema(Class<T> cls) {
Schema<T> schema = (Schema<T>) cachedSchema.get(cls);
if (schema == null) {
schema = RuntimeSchema.createFrom(cls);
cachedSchema.put(cls, schema);
}
return schema;
}
}
②基于fastjson实现:
maven依赖
<fastjson.version>1.2.3</fastjson.version>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>${fastjson.version}</version>
</dependency>
具体实现FastjsonSerializer.java
package org.laopopo.common.serialization.fastjson;
import org.laopopo.common.serialization.Serializer;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.parser.Feature;
import com.alibaba.fastjson.serializer.SerializerFeature;
/**
*
* @author BazingaLyn
* @description 使用fastjson序列化
* 需要有无参构造函数
* @time 2016年8月12日
* @modifytime
*/
public class FastjsonSerializer implements Serializer {
@Override
public <T> byte[] writeObject(T obj) {
System.out.println("FastjsonSerializer Serializer");
return JSON.toJSONBytes(obj, SerializerFeature.SortField);
}
@Override
public <T> T readObject(byte[] bytes, Class<T> clazz) {
System.out.println("FastjsonSerializer Deserializer");
return JSON.parseObject(bytes, clazz, Feature.SortFeidFastMatch);
}
}
③基于kryo实现
maven依赖
<kryo.version>2.21</kryo.version>
<dependency>
<groupId>com.esotericsoftware.kryo</groupId>
<artifactId>kryo</artifactId>
<version>${kryo.version}</version>
</dependency>
具体的实现KryoSerializer.java
package org.laopopo.common.serialization.kryo;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import org.laopopo.common.serialization.Serializer;
import com.esotericsoftware.kryo.Kryo;
import com.esotericsoftware.kryo.io.Input;
import com.esotericsoftware.kryo.io.Output;
import com.esotericsoftware.kryo.serializers.JavaSerializer;
/**
*
* @author BazingaLyn
* @description 使用Kryo序列化
* 需要实现java.io.Serializable接口
* @time 2016年8月12日
* @modifytime
*/
public class KryoSerializer implements Serializer {
@Override
public <T> byte[] writeObject(T obj) {
System.out.println("KryoSerializer serializer");
Kryo kryo = new Kryo();
kryo.setReferences(false);
kryo.register(obj.getClass(), new JavaSerializer());
ByteArrayOutputStream baos = new ByteArrayOutputStream();
Output output = new Output(baos);
kryo.writeClassAndObject(output, obj);
output.flush();
output.close();
byte[] b = baos.toByteArray();
try {
baos.flush();
baos.close();
} catch (IOException e) {
e.printStackTrace();
}
return b;
}
@SuppressWarnings("unchecked")
@Override
public <T> T readObject(byte[] bytes, Class<T> clazz) {
System.out.println("KryoSerializer deserializer");
Kryo kryo = new Kryo();
kryo.setReferences(false);
kryo.register(clazz, new JavaSerializer());
ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
Input input = new Input(bais);
return (T) kryo.readClassAndObject(input);
}
}
我们做个简单的测试,先定义一个要序列化的稍微复杂一点的类
package org.laopopo.example.netty;
import java.io.Serializable;
import org.laopopo.common.exception.remoting.RemotingCommmonCustomException;
import org.laopopo.common.transport.body.CommonCustomBody;
public class TestCommonCustomBody implements CommonCustomBody,Serializable {
/**
*
*/
private static final long serialVersionUID = 7679994718274344134L;
private int id;
private String name;
private ComplexTestObj complexTestObj;
public TestCommonCustomBody() {
}
public TestCommonCustomBody(int id, String name, ComplexTestObj complexTestObj) {
this.id = id;
this.name = name;
this.complexTestObj = complexTestObj;
}
public ComplexTestObj getComplexTestObj() {
return complexTestObj;
}
public void setComplexTestObj(ComplexTestObj complexTestObj) {
this.complexTestObj = complexTestObj;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public void checkFields() throws RemotingCommmonCustomException {
}
@Override
public String toString() {
return "TestCommonCustomBody [id=" + id + ", name=" + name + ", complexTestObj=" + complexTestObj + "]";
}
public static class ComplexTestObj implements Serializable {
/**
*
*/
private static final long serialVersionUID = 5694424296393939225L;
private String attr1;
private Integer attr2;
public ComplexTestObj() {
}
public ComplexTestObj(String attr1, Integer attr2) {
super();
this.attr1 = attr1;
this.attr2 = attr2;
}
public String getAttr1() {
return attr1;
}
public void setAttr1(String attr1) {
this.attr1 = attr1;
}
public Integer getAttr2() {
return attr2;
}
public void setAttr2(Integer attr2) {
this.attr2 = attr2;
}
@Override
public String toString() {
return "ComplexTestObj [attr1=" + attr1 + ", attr2=" + attr2 + "]";
}
}
}
测试类SerializerTest.java
package org.laopopo.example.serializer;
import org.laopopo.example.netty.TestCommonCustomBody;
import org.laopopo.example.netty.TestCommonCustomBody.ComplexTestObj;
import static org.laopopo.common.serialization.SerializerHolder.serializerImpl;
/**
*
* @author BazingaLyn
* @description
*
* 1)使用protoStuff序列化测试
* 修改org.laopopo.common.serialization.Serializer中的内容为:
* org.laopopo.common.serialization.proto.ProtoStuffSerializer
*
* 2)使用fastjson序列化测试
* 修改org.laopopo.common.serialization.Serializer中的内容为:
* org.laopopo.common.serialization.fastjson.FastjsonSerializer
*
* 3)使用kryo序列化测试
* 修改org.laopopo.common.serialization.Serializer中的内容为:
* org.laopopo.common.serialization.kryo.KryoSerializer
*
* @time 2016年8月12日
* @modifytime
*/
public class SerializerTest {
public static void main(String[] args) {
long beginTime = System.currentTimeMillis();
for(int i = 0;i < 100000;i++){
ComplexTestObj complexTestObj = new ComplexTestObj("attr1", 2);
TestCommonCustomBody commonCustomHeader = new TestCommonCustomBody(1, "test",complexTestObj);
byte[] bytes = serializerImpl().writeObject(commonCustomHeader);
TestCommonCustomBody body = serializerImpl().readObject(bytes, TestCommonCustomBody.class);
}
long endTime = System.currentTimeMillis();
System.out.println((endTime - beginTime));
}
}
三次运行的结果是:
在这边并没有很规范的去测试性能,不过,看起结果至少在一个数量级上,这就是序列化部分的内容
本节END~ 如果有错误的地方欢迎纠正