不灭的焱

革命尚未成功,同志仍须努力

作者:php-note.com  发布于:2018-10-17 00:12  分类:Java库/系统 

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