说到序列化,这在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~ 如果有错误的地方欢迎纠正