[原]高性能的序列化与反序列化:kryo的简单使用

Author Avatar
eguid 2月 12, 2018
  • 在其它设备中阅读本文章

前言:kryo是个高效的java序列化/反序列化库,目前Twitter、yahoo、Apache、strom等等在使用该技术,比如Apache的spark、hive等大数据领域用的较多。

为什么使用kryo而不是其他?

因为性能足够好。比kyro更高效的序列化库就只有google的protobuf了(而且两者性能很接近),protobuf有个缺点就是要传输的每一个类的结构都要生成对应的proto文件(也可以都放在同一个proto文件中,如果考虑到扩展性的话,不建议放在一个proto文件中),如果某个类发生修改,还得重新生成该类对应的proto文件;另外考虑到项目中用的全部是java技术栈,不存在不同编程语言间的兼容性问题,因此最终采用了kryo作为序列化库。

使用场景:(数据交换或数据持久化)比如使用kryo把对象序列化成字节数组发送给消息队列或者放到redis等nosql中等等应用场景。

注意:由于kryo不是线程安全的,针对多线程情况下的使用,要对kryo进行一个简单的封装设计,从而可以多线程安全的使用序列化和反序列化

序列化和反序列化接口设计

/**
 * 序列化工具(程序调用该接口来实现obj<->byte[]之间的序列化/反序列化)
 * @author eguid
 *
 */
public interface Serializer{

    /**
     * 序列化
     * @param t
     * @param bytes
     */
    public void serialize(Object t,byte[] bytes);

    /**
     * 序列化
     * @param obj
     * @param bytes
     * @param offset
     * @param count
     */
    public void serialize(Object obj, byte[] bytes, int offset, int count);

    /**
     * 反序列化
     * @param bytes -字节数组
     * @return T<T>
     */
    public <T>T deserialize(byte[] bytes);

    /**
     * 反序列化
     * @param bytes
     * @param offset
     * @param count
     * @return
     */
    public <T>T deserialize(byte[] bytes, int offset, int count);

}

使用kryo实现上面的接口

/**
 * 基于kyro的序列化/反序列化工具
 * 
 * @author eguid
 *
 */
public class kryoSerializer implements Serializer {

    // 由于kryo不是线程安全的,所以每个线程都使用独立的kryo
    final ThreadLocal<Kryo> kryoLocal = new ThreadLocal<Kryo>() {
        @Override
        protected Kryo initialValue() {
            Kryo kryo = new Kryo();
            kryo.register(ct, new BeanSerializer<>(kryo, ct));
            return kryo;
        }
    };
    final ThreadLocal<Output> outputLocal = new ThreadLocal<Output>();
    final ThreadLocal<Input> inputLocal = new ThreadLocal<Input>();
    private Class<?> ct = null;

    public kryoSerializer(Class<?> ct) {
        this.ct = ct;
    }

    public Class<?> getCt() {
        return ct;
    }

    public void setCt(Class<?> ct) {
        this.ct = ct;
    }

    @Override
    public void serialize(Object obj, byte[] bytes) {
        Kryo kryo = getKryo();
        Output output = getOutput(bytes);
        kryo.writeObjectOrNull(output, obj, obj.getClass());
        output.flush();
    }

    @Override
    public void serialize(Object obj, byte[] bytes, int offset, int count) {
        Kryo kryo = getKryo();
        Output output = getOutput(bytes, offset, count);
        kryo.writeObjectOrNull(output, obj, obj.getClass());
        output.flush();
    }

    /**
     * 获取kryo
     * 
     * @param t
     * @return
     */
    private Kryo getKryo() {
        return kryoLocal.get();
    }

    /**
     * 获取Output并设置初始数组
     * 
     * @param bytes
     * @return
     */
    private Output getOutput(byte[] bytes) {
        Output output = null;
        if ((output = outputLocal.get()) == null) {
            output = new Output();
            outputLocal.set(output);
        }
        if (bytes != null) {
            output.setBuffer(bytes);
        }
        return output;
    }

    /**
     * 获取Output
     * 
     * @param bytes
     * @return
     */
    private Output getOutput(byte[] bytes, int offset, int count) {
        Output output = null;
        if ((output = outputLocal.get()) == null) {
            output = new Output();
            outputLocal.set(output);
        }
        if (bytes != null) {
            output.writeBytes(bytes, offset, count);
        }
        return output;
    }

    /**
     * 获取Input
     * 
     * @param bytes
     * @param offset
     * @param count
     * @return
     */
    private Input getInput(byte[] bytes, int offset, int count) {
        Input input = null;
        if ((input = inputLocal.get()) == null) {
            input = new Input();
            inputLocal.set(input);
        }
        if (bytes != null) {
            input.setBuffer(bytes, offset, count);
        }
        return input;
    }

    @SuppressWarnings("unchecked")
    @Override
    public <T> T deserialize(byte[] bytes, int offset, int count) {
        Kryo kryo = getKryo();
        Input input = getInput(bytes, offset, count);
        return (T) kryo.readObjectOrNull(input, ct);
    }

    @Override
    public <T> T deserialize(byte[] bytes) {
        return deserialize(bytes, 0, bytes.length);
    }

测试一下kryo的序列化和反序列化

为什么使用纳秒,而不用毫秒?与java原生的序列化反序列化要耗时几毫秒不同,kryo序列化和反序列化太快了,单个对象的序列化反序列化速度都在0.0x毫秒左右(如果电脑性能更好的话,会更快)

Serializer ser = new kryoSerializer(Msg.class);
for (int i = 0; i < 10; i++) {

    Msg msg = new Msg();

    msg.setVersion_flag(new byte[] { 1, 2, 3 });
    msg.setCrc_code((short) 1);
    msg.setMsg_body(new byte[] { 123, 123, 123, 43, 42, 1, 12, 45, 57, 98 });
    byte[] bytes = new byte[300];
    long start = System.nanoTime();
    ser.serialize(msg, bytes);
    System.err.println("序列化耗时:" + (System.nanoTime() - start));
    System.out.println(msg);
    System.out.println(Arrays.toString(bytes));

    Msg newmsg = null;
    start = System.nanoTime();
    newmsg = ser.deserialize(bytes);
    System.err.println("反序列化耗时:" + (System.nanoTime() - start));
    System.out.println(newmsg);
}</pre>----end----

            <div>
                作者:eguid_1 发表于 2018/02/12 14:38:49 [原文链接](https://blog.csdn.net/eguid_1/article/details/79316403) https://blog.csdn.net/eguid_1/article/details/79316403                    </div>
            <div>
                阅读:165                     </div>

知识共享许可协议
本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。

本文链接:https://blog.eguid.cc/2018/02/12/原-高性能的序列化与反序列化:kryo的简单使用/