当前位置: 首页>后端>正文

Kryo 序列化说明

简介

Kroy 是一个相对来说性能和序列化以后的大小,都是比较好的一个序列化框架,项目 git 地址:

https://github.com/EsotericSoftware/kryo

简单的一个 demo :

         Log.set(1);   // 这里是开启kroy 的 trace的日志功能,可以看到具体的序列化和反序列化过程。

        Kryo kryo = new Kryo();

        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();

        Output output = new Output(outputStream);

        User user = new User();

        user.setName("test");
        user.setId(12);
        user.setPassword("test");

        List list = new ArrayList();
        list.add("test");
        list.add("test");
        list.add("p");

        user.setOrders(list);

        kryo.writeObject(output, user);
//        kryo.writeClassAndObject(output, user);
        output.close();

        byte[] bytes = outputStream.toByteArray();

//        System.out.println("string == " + bytesToHexString(bytes));

        System.out.println("===============  " + bytes.length);

        System.out.println(new String(bytes));

        System.out.println(binary(bytes , 16));

        ByteArrayInputStream inputStream = new ByteArrayInputStream(bytes);

        Input input = new Input(inputStream);

        User copy = kryo.readObject(input , User.class);

//        User copy = (User) kryo.readClassAndObject(input);
        input.close();

        System.out.println(user);
        System.out.println(copy);

说明:

有关序列化的字段:

kroy是根据对应的序列化实体的所有的字段 , 进行序列化操作,不会调用你设置的 实体的 set字段的方法。
eg : 序列化的反序列化不会调用这里的setName方法。

    class User {
        String name ;

        public setName(){
                this.name = name + "_update";
        }

    }

必须要有一个空的构造方法:

如果你包含非空的构造方法,则,必须强制添加一个空的空的构造方法,可以是私有的构造方法。

优点说明:

有关 register 说明:

在 kroy中,提供了register功能,register到底是做什么用的 ?如果开启了kroy的日志功能,我们可以看到如下的输出:

:00 DEBUG: [kryo] Class not available: java.time.ZoneOffset
00:00 DEBUG: [kryo] Class not available: java.time.ZoneId
00:00 DEBUG: [kryo] Class not available: java.time.OffsetTime
00:00 DEBUG: [kryo] Class not available: java.time.OffsetDateTime
00:00 DEBUG: [kryo] Class not available: java.time.ZonedDateTime
00:00 DEBUG: [kryo] Class not available: java.time.Year
00:00 DEBUG: [kryo] Class not available: java.time.YearMonth
00:00 DEBUG: [kryo] Class not available: java.time.MonthDay
00:00 DEBUG: [kryo] Class not available: java.time.Period
00:00 TRACE: [kryo] Register class ID 0: int (com.esotericsoftware.kryo.serializers.DefaultSerializers$IntSerializer)
00:00 TRACE: [kryo] Register class ID 1: String (com.esotericsoftware.kryo.serializers.DefaultSerializers$StringSerializer)
00:00 TRACE: [kryo] Register class ID 2: float (com.esotericsoftware.kryo.serializers.DefaultSerializers$FloatSerializer)
00:00 TRACE: [kryo] Register class ID 3: boolean (com.esotericsoftware.kryo.serializers.DefaultSerializers$BooleanSerializer)
00:00 TRACE: [kryo] Register class ID 4: byte (com.esotericsoftware.kryo.serializers.DefaultSerializers$ByteSerializer)
00:00 TRACE: [kryo] Register class ID 5: char (com.esotericsoftware.kryo.serializers.DefaultSerializers$CharSerializer)
00:00 TRACE: [kryo] Register class ID 6: short (com.esotericsoftware.kryo.serializers.DefaultSerializers$ShortSerializer)
00:00 TRACE: [kryo] Register class ID 7: long (com.esotericsoftware.kryo.serializers.DefaultSerializers$LongSerializer)
00:00 TRACE: [kryo] Register class ID 8: double (com.esotericsoftware.kryo.serializers.DefaultSerializers$DoubleSerializer)
00:00 TRACE: [kryo] Register class ID 9: void (com.esotericsoftware.kryo.serializers.DefaultSerializers$VoidSerializer)

可以看到, 他把 java 内置的一些类型,注册了一个 ID, 这个 ID 标识具体的 字段采用什么方法序列化,可以减少序列化以后的大小。

具体可以看下面的过程:

00:00 TRACE: [kryo] Optimize ints: true
00:00 TRACE: [kryo] Field id: int
00:00 TRACE: [kryo] Field name: class java.lang.String
00:00 TRACE: [kryo] Field password: class java.lang.String
00:00 TRACE: [kryo] Field orders: interface java.util.List
00:00 TRACE: [kryo] Register class name: User (com.esotericsoftware.kryo.serializers.FieldSerializer)
00:00 TRACE: [kryo] FieldSerializer.write fields of class: User
00:00 TRACE: [kryo] Write field: name (User) pos=2
00:00 TRACE: [kryo] Write initial object reference 1: test
00:00 TRACE: [kryo] Write: test
00:00 TRACE: [kryo] Write field: orders (User) pos=7
00:00 TRACE: [kryo] Register class name: java.util.ArrayList (com.esotericsoftware.kryo.serializers.CollectionSerializer)
00:00 TRACE: [kryo] Write class name: java.util.ArrayList
00:00 TRACE: [kryo] Write initial object reference 2: [test, test, p]
00:00 DEBUG: [kryo] Write: [test, test, p]
00:00 TRACE: [kryo] Write class 1: String
00:00 DEBUG: [kryo] Write object reference 1: test
00:00 TRACE: [kryo] Write class 1: String
00:00 DEBUG: [kryo] Write object reference 1: test
00:00 TRACE: [kryo] Write class 1: String
00:00 TRACE: [kryo] Write initial object reference 3: p
00:00 TRACE: [kryo] Write: p
00:00 TRACE: [kryo] Write field: password (User) pos=38
00:00 DEBUG: [kryo] Write object reference 1: test
00:00 TRACE: [kryo] Object graph complete.

对于我们一些自定义的字段,我们也可以调用 kroy 的方法,主动注册:

com.esotericsoftware.kryo.Kryo#register(java.lang.Class, int)

主动编码一个 ID 出来。

** 说明:**

  • 这里的 ID 不能重复,
  • 如果序列化 和反序列化不是用的同一个 kryo 实例,这里的 ID 必须都要调用 register 方法注册相同的 ID。例如 RPC 调用中, 否则会出现反序列化出错的情况。

有关 writeObject , writeClassAndObject , readObject , readClassAndObject

  • writeObject 和 readObject 属于一对关系,采用 writeObject 序列化, 则必须 采用 readObject 反序列化。

  • writeClassAndObject 和 readClassAndObject 如此。

** 两个的区别: **

  • 在反序列化的时候, readObject 需要知道 序列化的具体的类的类型, readClassAndObject 不需要。
        public <T> T readObject (Input input, Class<T> type) 

        public Object readClassAndObject (Input input)  

  • 序列化以后的大小不一样:

由于 readClassAndObject 在反序列化的时候,不需要知道具体的实体的类型 , 所以 writeClassAndObject 序列化的时候,将对于的类的类的名字,添加到了序列化的流的头部。所以, writeClassAndObject 以后的字符流要比 readObject 大一些。

我们将序列化以后的 byte [] 打印出来如下: 由于是采用字符串打印,所以,有乱码正常。 writeClassAndObject 结果:

        bytes.length 54
        字符串 :   com.test.Use?   tes?  java.util.ArrayLis?        ?p 

writeObject 结果:

       bytes.length 39
      字符串 :    tes?  java.util.ArrayLis?        ?p 

bytes 数组的大小,一个是 54 , 一个是 39 , 差别还是比较大。

有关 references :

可以理解为引用,我举一个例子可以说明:

有一个如下的实例:

    class User {
           String name , 
           String password ,

            ……
    }

    我们在实例化的时候如下:  new User("name" , "name") ,  name  和 password 赋值相同,则,kroy在序列化的时候,第二个字段会采用类似指针的形式,指向对一个 name的值。

    可以减少序列化以后的字符流的大小。

有关并发的说明:

    kroy实例不是线程安全的,如果是多线程访问,建议采用线程池的形式:

    KryoFactory factory = new KryoFactory() {
  public Kryo create () {
    Kryo kryo = new Kryo();
    // configure kryo instance, customize settings
    return kryo;
  }
};
// Build pool with SoftReferences enabled (optional)
KryoPool pool = new KryoPool.Builder(factory).softReferences().build();
Kryo kryo = pool.borrow();
// do s.th. with kryo here, and afterwards release it
pool.release(kryo);

总结

了解这些,可以大概对 kroy 有个总体的认识和理解 。 更深入的认识还是建议参考官方文档。


https://www.xamrdz.com/backend/3wr1941209.html

相关文章: