欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

一些常用Java序列化框架的比较

程序员文章站 2022-06-25 15:53:56
概念 序列化:将Java对象转化为字节数组 反序列化:将字节数组转化为Java对象 在RPC应用中,进行跨进程远程调用的时候,需要使用特定的序列化技术,需要对进行网络传输的对象进行序列化和反序列化。 影响序列化选择有两个因素 1. 序列化之后码流的大小,如果太大,那么将会影响网络传输的性能。 2. ......

概念

序列化:将java对象转化为字节数组

反序列化:将字节数组转化为java对象

在rpc应用中,进行跨进程远程调用的时候,需要使用特定的序列化技术,需要对进行网络传输的对象进行序列化和反序列化。

影响序列化选择有两个因素

1. 序列化之后码流的大小,如果太大,那么将会影响网络传输的性能。

2.     序列化和反序列化过程的性能

 

常用的序列化框架性能比较

 

一些常用Java序列化框架的比较

 

本文主要进行以下序列化框架的对比测试:

  • jdk
  • fastjson
  • hessian
  • protostuff

准备

需要序列化的对象,这是一个复杂的对象。

nettymessage
public class nettymessage  implements serializable {

    //消息头
    private header header;
    //消息体
    private object body;
}

@data
public class header implements serializable {

    //校验头
    private int crccode;

    //消息头消息体的总长度
    private  int length;

    //全局唯一id
    private  long sessionid;

    //消息类型
    private  messagetype type;

    //扩展字段
    private map<string,object> attachment;
}

@data
public class rpcrequest implements serializable {
    private long requestid;  //请求id
    private string interfacename;  //调用类名
    private string methodname; //调用方法名
    private string[] parametertypes; //方法参数类型
    private object[] parameters;   //方法参数


}

 

 

创建一个构造器创建该对象。

public class nettymessagebuilder {

    public  static nettymessage build(){

        nettymessage message = new nettymessage();
        header header = new header();
        rpcrequest request = new rpcrequest();

        header.setcrccode(1234);
        header.settype(messagetype.app_respone_type);
        header.setlength(100);
        header.setsessionid(200);

        map<string,object> map = new linkedhashmap<>();

        map.put("demokey",(object)"demovalue");
        header.setattachment(map);


        request.setinterfacename("com.demo");
        string[] types = {"java.lang.string" ,"java.lang.integer"};
        string[] param = {"java.lang.string" ,"java.lang.integer"};
        request.setparametertypes(types);
        request.setparameters(param);
        request.setmethodname("buy");
        request.setrequestid(123456);


        message.setheader(header);
        message.setbody(request);

        return  message;
    }

}

 

定义序列化接口

public abstract class abstractserialize {

    public  abstract   <t> byte[] serialize(t obj);
    public abstract  <t> t deserialize(byte[] data, class<t> clazz);


}

 

jdk

实现

public class jdkserializeutil extends abstractserialize {

    public <t> byte[] serialize(t obj) {

        if (obj  == null){
            throw new nullpointerexception();
        }

        bytearrayoutputstream bos = new bytearrayoutputstream();
        try {
            objectoutputstream oos = new objectoutputstream(bos);

            oos.writeobject(obj);
            return bos.tobytearray();
        } catch (exception ex) {
            ex.printstacktrace();
        }
        return new byte[0];
    }

    public <t> t deserialize(byte[] data, class<t> clazz) {
        bytearrayinputstream bis = new bytearrayinputstream(data);

        try {
            objectinputstream ois = new objectinputstream(bis);
            t obj = (t)ois.readobject();
            return obj;
        } catch (exception ex) {
            ex.printstacktrace();
        }

        return  null;
    }
}

 

 

fastjson

引入pom

 <dependency>
     <groupid>com.alibaba</groupid>
     <artifactid>fastjson</artifactid>
     <version>1.2.56</version>
 </dependency>
            

 

实现

public class fastjsonserializeutil  extends abstractserialize {

    public <t> byte[] serialize(t obj) {
        if (obj  == null){
            throw new nullpointerexception();
        }

        string json = json.tojsonstring(obj);
        byte[] data = json.getbytes();
        return data;
    }

    public <t> t deserialize(byte[] data, class<t> clazz) {

        t obj = json.parseobject(new string(data),clazz);
        return obj;
    }
}

 

hessian

<dependency>
    <groupid>com.caucho</groupid>
    <artifactid>hessian</artifactid>
    <version>4.0.60</version>
 </dependency>

 

实现

@slf4j
public class hessianserializeutil extends abstractserialize {



    public <t> byte[] serialize(t obj) {

        if (obj  == null){
            throw new nullpointerexception();
        }
        try{
            bytearrayoutputstream bos = new bytearrayoutputstream();
            hessianoutput ho = new hessianoutput(bos);
            ho.writeobject(obj);

            return  bos.tobytearray();
        }
        catch(exception ex){
            log.error("hessianserializeutil序列化发生异常!"+ex);
            throw new  runtimeexception();
        }

    }

    public <t> t deserialize(byte[] data, class<t> clazz) {

        if (data == null){
            throw  new  nullpointerexception();
        }
        try{
            bytearrayinputstream bis = new bytearrayinputstream(data);
            hessianinput hi = new hessianinput(bis);
            return (t)hi.readobject();

        }
        catch(exception ex){
            log.error("hessianserializeutil反序列化发生异常!"+ex);
            throw new  runtimeexception();
        }

    }
}

 

 

protostuff

<dependency>
    <groupid>io.protostuff</groupid>
    <artifactid>protostuff-core</artifactid>
    <version>1.6.0</version>
     <scope>compile</scope>
</dependency>


<!-- https://mvnrepository.com/artifact/io.protostuff/protostuff-runtime -->
 <dependency>
    <groupid>io.protostuff</groupid>
    <artifactid>protostuff-runtime</artifactid>
    <version>1.6.0</version>
</dependency>

 

实现

public class protostuffserializeutil  extends abstractserialize {

    /**
     * 避免每次序列化都重新申请buffer空间
     */
    private static linkedbuffer buffer = linkedbuffer.allocate(linkedbuffer.default_buffer_size);
    /**
     * 缓存schema
     */
    private static map<class<?>, schema<?>> schemacache = new concurrenthashmap<class<?>, schema<?>>();

    public   <t> byte[] serialize(t obj) {

        if (obj  == null){
            throw new nullpointerexception();
        }
        class<t> clazz = (class<t>) obj.getclass();
        schema<t> schema = getschema(clazz);
        byte[] data;
        try {
            data = protostuffioutil.tobytearray(obj, schema, buffer);
        } finally {
            buffer.clear();
        }

        return data;
    }

    public <t> t deserialize(byte[] data, class<t> clazz) {
        schema<t> schema = getschema(clazz);
        t obj = schema.newmessage();
        protostuffioutil.mergefrom(data, obj, schema);
        return obj;
    }


    private static <t> schema<t> getschema(class<t> clazz) {
        schema<t> schema = (schema<t>) schemacache.get(clazz);
        if (schema == null) {
            //这个schema通过runtimeschema进行懒创建并缓存
            //所以可以一直调用runtimeschema.getschema(),这个方法是线程安全的
            schema = runtimeschema.getschema(clazz);
            if (schema != null) {
                schemacache.put(clazz, schema);
            }
        }

        return schema;
    }


}

 

 

测试

测试方法

 @test
    public void testfastjsonserialize(){

     //这里替换各种序列化实现类 abstractserialize serialize = new protostuffserializeutil(); nettymessage message = nettymessagebuilder.build(); timeutil timeutil = new timeutil(); timeutil timeutil1 = new timeutil(); nettymessage result = null; byte[] serbyte = serialize.serialize(message); system.out.println("字节长度:" + serbyte.length); result = serialize.deserialize(serbyte,nettymessage.class);
     //这里设置测试次数 for(int i = 0; i< 100000; i++){ //timeutil.init(); timeutil.start(); serbyte = serialize.serialize(message); timeutil.end(); //system.out.println("序列化时间:"+ timeutil.getavrtimeus() + " us"); timeutil1.start(); result = serialize.deserialize(serbyte,nettymessage.class); timeutil1.end(); } system.out.println("序列化时间:"+ timeutil.getavrtimeus() + " us"); system.out.println("反序列化时间:"+ timeutil1.getavrtimeus() + " us"); system.out.println("结果:" + result); }

 

这里定义了一个timeutil类来计时

 

public class timeutil {

    private  long starttime;
    private  long endtime;
    private  long timesum;
    private  long count;

    public  void init(){
        timesum = 0;
        count = 0;
    }

    public    void start(){
        starttime = system.nanotime();

    }

    public  void end(){
        endtime = system.nanotime();
        timesum += (endtime-starttime);
        count++;
    }

    public   long getavrtimens(){
        return (timesum/count);
    }
    public   long getavrtimeus(){
        return (timesum/count)/1000;
    }

    public   long getavrtimems(){
        return (timesum/count)/1000000;
    }
    
}

 

 

  码流大小(byte) 10次(us) 100次(us) 1000次(us) 10000次(us) 100000次(us)  
fastjson 305 116-243 106-185 90-140 26-39 8-12  
jdk 866 383-777 502-1101 123-334 54-237 15-76  
hessian 520 959-3836 376-567 191-329 99-161 30-47  
protostuff 193 103-145 90-137 75-135 15-24 5-8  
               

 

 

 

 

 

 

 

 

 

注:

1. 码流单位为字节

2. 序列化耗时-反序列化耗时,单位为微秒

从以上测试可以看出

1. jdk方式的码流最大,不利于网络传输。

2. 从整体来看,prorostuff的码流最小,序列化性能最好。