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


    概念

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

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

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

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

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

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

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

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

    • 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的码流最小,序列化性能最好。

  • 相关阅读:
    熬夜到凌晨2点半

    浏览器F12,Network中各按钮的作用
    postman收藏 -大佬玩法。
    Windows日常快捷键
    认识jmeter(一)
    web测试:test过程中接口报错 "Object reference not set to an instance of an object."
    postman-error:SyntaxError: Invalid shorthand property initializer
    mysql 表结构及基本操作
    getinstance方法(转)
  • 原文地址:https://www.cnblogs.com/lgjlife/p/10731099.html
Copyright © 2020-2023  润新知