您的位置:首页 > 理论基础 > 计算机网络

一起写RPC框架(五)RPC网络模块的搭建三 序列化

2016-10-19 14:30 337 查看
 说到序列化,这在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~ 如果有错误的地方欢迎纠正
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐