分布式服务框架-序列化与反序列化

824 阅读5分钟

这是我参与8月更文挑战的第18天,活动详情查看: 8月更文挑战

本文使用源码地址:simple-rpc 把对象转换为字节序列的过程称为对象的序列化把字节序列恢复为对象的过程称为对象的反序列化

序列化的主要目的:

  • 通过将对象序列化为字节数组,是的不共享内存通过网络连接的系统之间能够进行对象的传输。
  • 通过对象序列化为字节数组,能够将对象永久存储到存储设备。
  • 解决远程接口调用JVM之间内存无法共享问题。

各种常用的序列化的性能对比可以查看开源项目:jvm-serializers

simple-rpc中只实现了其中一部分:

Serializer.png

为了抽象出一个序列化/反序列化的通用服务,首先定义序列化/反序列化的通用接口。代码如下:

public interface ISerializer {

    /**
     * 序列化
     * @param obj
     * @param <T>
     * @return
     */
    <T> byte[] serialize(T obj);

    /**
     * 反序列化
     * @param data
     * @param clazz
     * @param <T>
     * @return
     */
    <T> T deserialize(byte[] data, Class<T> clazz);
}

下面基于该接口,实现上述五种常用的序列化/反序列化方式。

Java默认的序列化

JDK提供了Java对象的序列化方式。Java的序列化主要通过对象输出流java.io.ObjectOutputStream与对象输入流java.io.ObjectInputStream来实现,其中被序列化的类需要实现java.io.Serializable接口。代码如下:

@Slf4j
public class DefaultSerializer implements ISerializer {

    @Override
    public <T> byte[] serialize(T obj) {
        if (obj == null) {
            throw new NullPointerException("DefaultSerializer serialize data is null");
        }
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();

        try {
            ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
            objectOutputStream.writeObject(obj);
            objectOutputStream.close();
        } catch (IOException e) {
            log.error("DefaultSerializer serialize error, obj={}", obj, e);
            throw new SRpcException("DefaultSerializer serialize error", e);
        }

        return byteArrayOutputStream.toByteArray();
    }

    @Override
    public <T> T deserialize(byte[] data, Class<T> clazz) {
        if (data == null) {
            throw new NullPointerException("DefaultSerializer deserialize data is null");
        }
        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(data);

        try {
            ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream);
            return (T) objectInputStream.readObject();
        } catch (Exception e) {
            log.error("DefaultSerializer deserialize error, data={}", data, e);
            throw new SRpcException("DefaultSerializer deserialize error", e);
        }
    }
}

对于Java序列化有如下知识点:

  • 序列化时,只对对象的状态进行保存,而不管对象的发方法。
  • 当一个父类实现序列化,子类自动实现序列化,不需要显示实现java.io.Serializable接口。
  • 当一个对象的实例变量引用其他对象,序列化改对象时也把引用对象进行序列化。
  • 当某个对象被声明为transient,默认序列化机制会忽略该字段。

Java默认的序列化机制优缺点非常明显:

优点:

  • Java语言自带,简单易用,无需引入第三方依赖。

缺点:

  • 只支持Java语言,不支持跨语言。
  • Java默认序列化性能欠佳,序列化后产生的码流过大,对于引用过深的对象序列化易发生内存OOM异常。

Json序列化

Json是一种轻量级的数据交换格式。Json码流较小,并且可读性好。Json常用的序列化开源工具如下:

  • Jackson
  • fastjson
  • Gson

为了篇幅可控,我们看其中fastjson的实现方式,代码如下:

public class FastJsonSerializer implements ISerializer {

    static {
        ParserConfig.getGlobalInstance().addAccept("com.miracle.srpc");
    }

    @Override
    public <T> byte[] serialize(T obj) {
        if (obj  == null) {
            throw new NullPointerException("FastJsonSerializer serialize data is null");
        }
        JSON.DEFFAULT_DATE_FORMAT = "yyyy-MM-dd HH:mm:ss";
        return JSON.toJSONString(obj, SerializerFeature.WriteDateUseDateFormat, SerializerFeature.WriteClassName).getBytes();
    }

    @Override
    public <T> T deserialize(byte[] data, Class<T> clazz) {
        if (data  == null) {
            throw new NullPointerException("FastJsonSerializer deserialize data is null");
        }
        return JSON.parseObject(new String(data), clazz);
    }
}

Hessian序列化

Hessian是一个支持跨语言传输的二进制序列化协议。相对于Java默认的序列化机制,Hessian具有更好的性能与易用性,而且支持多种不同的语言。序列化与反序列化实现如下:

@Slf4j
public class HessianSerializer implements ISerializer {

    @Override
    public <T> byte[] serialize(T obj) {
        if (obj == null) {
            throw new NullPointerException("HessianSerializer serialize obj is null");
        }
        try {
            ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
            HessianOutput output = new HessianOutput(byteArrayOutputStream);
            output.writeObject(obj);
            return byteArrayOutputStream.toByteArray();
        } catch (IOException e) {
            log.error("HessianSerializer serialize error. obj = {}", obj, e);
            throw new SRpcException("HessianSerializer serialize error", e);
        }
    }

    @Override
    public <T> T deserialize(byte[] data, Class<T> clazz) {
        if (data  == null) {
            throw new NullPointerException("HessianSerializer deserialize data is null");
        }
        try {
            ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(data);
            HessianInput hessianInput = new HessianInput(byteArrayInputStream);
            return (T) hessianInput.readObject();
        } catch (IOException e) {
            log.error("HessianSerializer deserialize error. data = {}", data, e);
            throw new SRpcException("HessianSerializer deserialize error", e);
        }
    }
}

protobuf序列化

protobuf是Google开源的一种数据交换格式,支持多种语言,对于每一种实现都包含了相应语言的编译器和库文件。protobuf空间开销小,解析性能高,序列化后数据量少,但是它需要编写*.proto IDL文件。

使用protobuf步骤如下:

  1. 配置开发环境,安装Protocol Compiler代码编译器。
  2. 编写.proto文件,定义序列化对象的数据结构。
  3. 基于编写.proto文件,使用Protocol Compiler编译生成对应的序列化/反序列工具类。
  4. 基于生成的代码,编写自己的序列化应用。

序列化与反序列化实现如下:

@Slf4j
public class ProtoBufSerializer implements ISerializer {

    @Override
    public <T> byte[] serialize(T obj) {
        try {
            if (obj instanceof GeneratedMessageV3) {
                throw new UnsupportedOperationException("ProtoBufSerializer serialize not support obj type");
            }
            return (byte[]) MethodUtils.invokeMethod(obj, "toByteArray");
        } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
            log.error("ProtoBufSerializer serialize error, obj={}", obj, e);
            throw new SRpcException("ProtoBufSerializer serialize error", e);
        }
    }

    @Override
    public <T> T deserialize(byte[] data, Class<T> clazz) {
        try {
            if (!GeneratedMessageV3.class.isAssignableFrom(clazz)) {
                throw new UnsupportedOperationException("ProtoBufSerializer deserialize not support obj type");
            }
            return (T) MethodUtils.invokeStaticMethod(clazz, "getDefaultInstance");
        } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
            log.error("ProtoBufSerializer deserialize error", e);
            throw new SRpcException("ProtoBufSerializer deserialize error", e);
        }
    }
}

序列化引擎

根据不同的序列化框架,提供上述几种序列化与反序列化实现,为了序列化工具更容易使用,下面将上述的实现整合为通用的序列化工具引擎,可以通过输入不同配置随意选择使用哪种序列化方案。代码实现如下:

@Slf4j
public class SerializerEngine {

    private static final Map<SerializeType, ISerializer> SERIALIZER_MAP = Maps.newHashMap();

    static {
        SERIALIZER_MAP.put(SerializeType.DefaultSerializer, new DefaultSerializer());
        SERIALIZER_MAP.put(SerializeType.JacksonSerializer, new JacksonSerializer());
        SERIALIZER_MAP.put(SerializeType.FastJsonSerializer, new FastJsonSerializer());
        SERIALIZER_MAP.put(SerializeType.HessianSerializer, new HessianSerializer());
        SERIALIZER_MAP.put(SerializeType.ProtoBufSerializer, new ProtoBufSerializer());
    }

    /**
     * 序列化
     * @param obj 对象
     * @param serializeType 序列化方式
     * @param <T>
     * @return
     */
    public static <T> byte[] serialize(T obj, String serializeType) {
        SerializeType serialize = SerializeType.getByType(serializeType);
        if (null == serialize) {
            throw new SRpcException("SerializerEngine serialize error, serializeType is not support");
        }
        ISerializer iSerializer = SERIALIZER_MAP.get(serialize);
        if (null == iSerializer) {
            throw new SRpcException("SerializerEngine serialize error, get ISerializer fail");
        }
        try {
            return iSerializer.serialize(obj);
        } catch (Exception e) {
            log.error("SerializerEngine serialize error, obj={}", obj, e);
            throw new SRpcException("SerializerEngine serialize error");
        }
    }

    /**
     * 反序列化
     * @param data 数据
     * @param clazz
     * @param serializeType
     * @param <T>
     * @return
     */
    public static <T> T deserialize(byte[] data, Class<T> clazz, String serializeType) {
        SerializeType serialize = SerializeType.getByType(serializeType);
        if (null == serialize) {
            throw new SRpcException("SerializerEngine deserialize error, serializeType is not support");
        }
        ISerializer iSerializer = SERIALIZER_MAP.get(serialize);
        if (null == iSerializer) {
            throw new SRpcException("SerializerEngine deserialize error, get ISerializer fail");
        }
        try {
            return iSerializer.deserialize(data, clazz);
        } catch (Exception e) {
            log.error("SerializerEngine deserialize error, data={}", data, e);
            throw new SRpcException("SerializerEngine deserialize error");
        }
    }
}

SerializerEngine通过静态代码块,在类加载时将序列化算法添加到本地缓存中,提供了相应的序列化与反序列方法,并且提供了可传入参数serializeType,做到序列化与反序列化的可配置化。