rpc 之序列化

序列化的概念

序列化:把对象转化为字节序列的过程。

反过来说就是反序列化。


序列化的应用

1、储存对象,可以是永久的储存在硬盘的一个文件上,也可以是储存在redis支持序列化存储的容器中。
2、网络上远程传输对象。

Rpc之间调用以流字节的方式传输,java序列化是java平台上的序列化工具,一个对象需要在网络上传输,需实现 Serializable接口

@Data
@AllArgsConstructor
public class Pojo implements Serializable {

    public String name;
    public Integer age;
    public Address address; //  transient 不被序列化

    public static void main(String[] args) throws IOException, ClassNotFoundException {

        Address address = new Address("zj", "hz");
        Pojo pojo = new Pojo("van", 22, address);
        // 序列化到磁盘Io
        try (FileOutputStream fos = new FileOutputStream("pojo");
             ObjectOutputStream oos = new ObjectOutputStream(fos)) {
            oos.writeObject(pojo);
            oos.flush();
        }

        try (FileInputStream fis = new FileInputStream("pojo");
             ObjectInputStream ois = new ObjectInputStream(fis);
        ) {
            Pojo p = (Pojo) ois.readObject();
            System.out.println(p);
        }


    }



}

@Data
@AllArgsConstructor
public  class Address implements Serializable {
    public String province;
    public String city;
}


以上代码会把对象序列化,存在磁盘上
内容为�� sr !org.spafka.serializable.java.Pojo����FH�� L addresst &Lorg/spafka/serializable/java/Address;L aget Ljava/lang/Integer;L namet Ljava/lang/String;xpsr $org.spafka.serializable.java.AddressۦX%o�� L cityq ~ L provinceq ~ xpt hzt zjsr java.lang.Integer⠤���8 I valuexr java.lang.Number��� ���  xp   t van

大致看得懂

对序列化过程做基准测试
Address address = new Address("zj", "hz");
Pojo pojo = new Pojo("van", 22, address);


long start = System.currentTimeMillis();
IntStream.rangeClosed(0, 1000000).forEach((i) -> {
    byte[] bytes = null;
    try (ByteArrayOutputStream baos = new ByteArrayOutputStream();
         ObjectOutputStream oos = new ObjectOutputStream(baos);) {

        oos.writeObject(pojo); //335个字节长度
        bytes = baos.toByteArray();
    } catch (IOException e) {
        e.printStackTrace();
    }

    //     System.out.println(bytes.length);
    try (ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
         ObjectInputStream ois = new ObjectInputStream(bais);) {
        Pojo p = (Pojo) ois.readObject();
    } catch (IOException e) {
        e.printStackTrace();
    } catch (ClassNotFoundException e) {
        e.printStackTrace();
    }
});

System.out.println(System.currentTimeMillis() - start);  //15630

一百万的对象的序列化反序列化花了15s,每个序列化后的大小为335个字节  对比其他序列化方式,不得不说有点弱。


protobuf是谷歌开发的一套序列化工具
谷歌出品,必属精品。

在java上开发protostuff 很容易
添加依赖包

<dependency>
    <groupId>com.dyuproject.protostuff</groupId>
    <artifactId>protostuff-core</artifactId>
    <version>1.1.2</version>
</dependency>
<dependency>
    <groupId>com.dyuproject.protostuff</groupId>
    <artifactId>protostuff-runtime</artifactId>
    <version>1.1.2</version>
</dependency>
<dependency>
    <groupId>org.objenesis</groupId>
    <artifactId>objenesis</artifactId>
    <version>2.6</version>
</dependency>


调用起来也十分方便
public class ProtuStuffUtil {

    private static volatile Map <Class <?>, Schema <?>> cachedSchema = new ConcurrentHashMap <>();

    //schema 对象的描述信息,  构建schema的过程可能会比较耗时,因此希望使用过的类对应的schema能被缓存起来。代码如下,不再赘述:
    private static <T> Schema <T> getSchema(Class <T> cls) {
        Schema <T> schema = (Schema <T>) cachedSchema.get(cls);
        if (schema == null) {
            schema = RuntimeSchema.createFrom(cls);
            synchronized (ProtuStuffUtil.class) {
                if (schema != null) {
                    cachedSchema.put(cls, schema);
                }
            }
        }
        return schema;
    }

    private static Objenesis objenesis = new ObjenesisStd(true);

    //    3行:获得对象的类;
//    4行:使用LinkedBuffer分配一块默认大小的buffer空间;
//    6行:通过对象的类构建对应的schema//    7行:使用给定的schema将对象序列化为一个byte数组,并返回。
    @SuppressWarnings("unchecked")
    public static <T> byte[] serialize(T obj) {
        Class <T> cls = (Class <T>) obj.getClass();
        LinkedBuffer buffer = LinkedBuffer.allocate(LinkedBuffer.DEFAULT_BUFFER_SIZE);
        try {
            Schema <T> schema = getSchema(cls);
            return ProtostuffIOUtil.toByteArray(obj, schema, buffer);
        } catch (Exception e) {
            throw new IllegalStateException(e.getMessage(), e);
        } finally {
            buffer.clear();
        }
    }

    //
//    3行:使用objenesis实例化一个类的对象;
//    4行:通过对象的类构建对应的schema//    56行:使用给定的schemabyte数组和对象合并,并返回。
    public static <T> T deserialize(byte[] data, Class <T> cls) {
        try {
            T message = objenesis.newInstance(cls);
            Schema <T> schema = getSchema(cls);
            ProtostuffIOUtil.mergeFrom(data, message, schema);
            return message;
        } catch (Exception e) {
            throw new IllegalStateException(e.getMessage(), e);
        }
    }


}


对protostuff做基准测试

@Test
public void benchMarkProtusuff() {

    long start = System.currentTimeMillis();

    Address address = new Address("zj", "hz");
    Pojo pojo = new Pojo("van", 22, address);


    IntStream.rangeClosed(0, 1000000).forEach((i) -> {
        byte[] serialize = ProtuStuffUtil.serialize(pojo);
        //  System.out.println(serialize.length); //17个字节长度
        Pojo deserialize = ProtuStuffUtil.deserialize(serialize, Pojo.class);
    });
    System.out.println(System.currentTimeMillis() - start); //629

}
百万次的序列化只有629ms,其中大部分时间或在构造schema 上,序列化的大小也只有17byte,远远强于java自带的序列化,推荐开发高性能rpc服务上首选采用protustuff



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值