继续浏览精彩内容
慕课网APP
程序员的梦工厂
打开
继续
感谢您的支持,我会继续努力的
赞赏金额会直接到老师账户
将二维码发送给自己后长按识别
微信支付
支付宝支付

百万架构师第二十七课:分布式架构的基础:序列化和反序列化|JavaGuide

不止极客
关注TA
已关注
手记 41
粉丝 0
获赞 0
  1. 了解序列化的意义
  2. 如何实现一个序列化操作
  3. 序列化的高阶认识
  4. 常见的序列化技术及应用
  5. Protobuf实现原理分析
  6. 序列化框架的选型

了解序列化的意义

我们非常地考虑系统性能的时候,就要考虑到序列化。

序列化在我们系统架构里边处于非常底层的位置。我们平时不会去关心,我们使用的是什么序列化。后者说我们用的一些RPC框架它是怎么去做的。

远程通信需要用到序列化。

序列化和反序列化是我们几乎每天面临的问题,

项目之间的调用都需要序列化。是我们必须要掌握的东西。

如果想把java对象传输给其他的进程。

JVM内部有一个内部的对象,需要传输。

对象的传输怎么去做

RPC做远程传输,不可能只传输字节流,还需要一些java对象。

在分布式架构系统中,序列化的性能会对我们系统性能有影响。所以我们要去关注序列化、从而提升整体的性能。

Socket传输一个流,可以简单地传输

当我们要传输List/Object等java对象时,怎么办?

Java中允许在内存中创建一些可复用的java对象,只有在JVM在运行时对象才会存在,然后JVM的生命周期限制了对象的生命周期。

序列化-对象传输怎么去做.png

发送端 序列化 把对象的状态信息转化成可存储可传输的形式的数据
接收端 反序列化 反过来

使用tomcat时,把session放在内存中,如果用户比较多的话,比如说10万,100万,假设,那我们会把session数据放在磁盘上,然后用的时候再反序列化。

远程传输

状态信息存储到磁盘上

序列化带来的挑战

  1.  我们希望序列化出来的数据越小越好,越小就越节省带宽,
    
  2.  序列化本身会占用我们的内存和资源,
    

序列化的快、慢;序列化出来的字节大、小。

Java实现序列化:serialization

Serialization

  1.  数据比较大
    
  2.  语言限制(跨语言跨平台)
    
  3.  Xml(soap)(WebService里面用得很多,比java生成出来的二进制的序列化方式更加容易理解)
    

JSON

简单文本格式的,基于restful风格的API逐步取代XML

Spring 4 以后自带基于jackJson的自动转化json的功能,

但是JSON,占用的空间也很大,而且性能也相对比较低。

跟用户打交道时,需要提升效率。

Protobufs messagepack

IDEA设置生成SerialVersionUID

  • setting
    • Editor
      • Inspections
        • serializable class without serialVersinUid : ✔️
@Data
public class User extends superClass implements Serializable {

    private static final long serialVersionUID = -281884127299001746L;
    //   private static final long serialVersionUID = -1L;
    private static Integer b = 5;

    private Integer age;
    private String name;
}

serialVersionUID作用

  • 版本的UID

    • 判断类是否一样的,
  • 默认地根据calss文件,生成对应的64位的hash字段。(我们看不到)

  • 编译的时候生成的

java.io.invalidCalssException: com.gupao.User. class compatible: : seriVersionUid : -3493499349L. local class serialVersionUid = -3
   at java................

面试题:

serialVersionUID不一致

假如序列化与反序列化的serialVersionUID 不一致,会报invalidCalssException错误。

假设两个serialVersionUID一致,

序列化的类是四个字段,反序列化的类是三个字段,两个serialVersionUID一样。

序列化的类是四个字段序列化。反序列化传输进去了三个字段,然后序列化不报错。

只不过是三个字段序列化,反序列化传出进去了四个字段,序列化不报错,为空。

静态变量的序列化

在类中定义一个静态变量。

Java序列化存储类的瞬时状态。

静态变量不参与序列化。

transient

Transient表示不会被序列化。

父子类关系

序列化的对象必须实现序列化,

子类实现序列化,父类不实现序列化,不能够被序列化

父类实现了序列化,子类不实现序列化也可以序列化。

可以自己写一个writeOjbect

手写序列化,反序列化 绕过 transient 实现序列化,手动地写到流里边。

序列化两次

一个序列化的文件大小,

对一个对象在同一个流里边输出两次,

在同一个流管道里输出两次并不会输出两次

第二次输出的是上一个对象的引用

加了5个字节,

指向同一个对象。

克隆

序列化可以实现克隆

Clonable 克隆接口,浅克隆

浅克隆

浅克隆,只克隆这个对象的所有的值,和他这个对象本身,只是一个引用。只是一个引用,并不是一个新的对象。引用的对象不会被复制。

它只是克隆当前这个对象本身和这个对象对应的值,但是这个对象里边成员变量里边的对象所指向的引用,它不会再去克隆。

public class CloneDemo {

    public static void main(String[] args) throws CloneNotSupportedException, 
    IOException, ClassNotFoundException {
        Email email = new Email();
        email.setContent("今天去钓鱼");
        Person p1 = new Person("A");
        p1.setEmail(email);

        //Person p2 = p1.clone();
        Person p2 = p1.deepClone();
        p2.setName("B");
        p2.getEmail().setContent("今天去打架");


        System.out.println(p1);
        System.out.println(p2);

    }
}
结果
Person(name=A, email=Email(content=今天去打架))
Person(name=B, email=Email(content=今天去打架))

深克隆。

// 用序列化实现深度克隆
// 手写深度克隆
public class xxxxx Implents serialable{
    //   序列化,方法

    // 反序列化 方法
}

深度克隆以后的结果

Person(name=A, email=Email(content=今天去钓鱼))
Person(name=B, email=Email(content=今天去打架))

传输对象,序列化

序列化-模块之间通过协议传输.png

常见的序列化技术

考虑因素

  1.  序列化结果的数据大小
    
  2.  序列化占用CPU,内存   性能
    
  3.  序列化的复杂度。实现起来是否复杂,学习成本
    

XML形式,

序列化,可读性非常高,效率低,数据量大。

应用:webService 中的soap (http+xml)传输的形式

性能要求很高的时候不会选用XML技术。

<dependency>
    <groupId>com.thoughtworks.xstream</groupId>
    <artifactId>xstream</artifactId>
    <version>1.4.9</version>
</dependency>

基于 OXMapping 的一个实现方法

基于JSON实现序列化

Jackson spring 默认使用
fastjson
比GSON的性能要好
Fastjson 据说最快
不太稳定
GSON 谷歌的GSON

Jackson、GSON 比 fastjson 的稳定性要好

  • Hession 序列化 hession2
  • Dubbo默认使用hession,对 hession实现了一个优化,生成流的时候做了一个压缩。Hession2实现了多个序列化的选择。

所有序列化生成的数据格式不一样

  • hession2 对 hession做了一个优化,他的性能也是不错的。

Dobbo提供的序列化

  • ConpactedJavaSerializatioin 压缩的java序列化

  • DubboSerialization

  • FastJsonSerialization

  • Hession2Serialization

  • JavaSerialization

  • JsonSerialization

  • NativeJavaSerialization

  • Kyio 和 protobuf 都不能选择

Protobuf序列化框架

优点

  1. 谷歌开源
  2. 独立语言、独立平台
    • (基于Java,基于C,基于C++,基于pathon,它可以去做不同平台的交互)
    • 纯粹地基于表示层的协议,它可以和各种传输层协议一起使用。
  3. 压缩以后空间开销小,性能比较好
    • 性能要求高的话,需要用protobuf
  4. 解析性能比较高

缺点

Protobuf 学习成本比较高,使用起来比较麻烦。

Github上有独立的编译器,

  1.  下载编译器
    
  2.  编写独立的proto文件(自己的语法)
    
  3.  编译(通过编译器编译成java版本)
    

下载:

  1.  创建文本  `user.proto` 
    

定义一个一个proto文件,

Syntax = “proto2”;

Package com.gupaoedu.serial;

 
Option java_package=”com.gupaoedu.serial”
Option java_outer_classname=”com.gupaoedu.serial”

文件格式

编译命令

  • Shirft + 右键 打开 powerShell

Fluent风格,实现序列化

粘贴进去.java文件

public class protoBufDemo {

    public static void main(String[] args) throws InvalidProtocolBufferException {
        // fluent 风格
        UserProto .User user = UserProto.User.newBuilder().setName("Darian").setAge(18).build();

        ByteString bytes = user.toByteString();
        System.out.println(bytes);

        UserProto.User nUser = UserProto.User.parseFrom(bytes);
        System.out.println(nUser);

    }
}
<ByteString@20sdfsfd2 size=10>
name: "darian"
age: 18

Protobuf 原理解析

Protobuf 为什么序列化的字节数那么小,

利用了位运算

BitMap/BitSet

一个int 4个字节,一个字节8个byte位。

序列化-一个INT-8个字节.png

  1. Varint做编码

  2. T-L-V做存储

Protobuf存储占用的空间很小而且你存的数越小,它占用的空间数越小。

数字和字符串都要转化成编码 去存储。

Int 32 = 296 【23个0】 100101000

前边的23个0全部是浪费的。

Varint有自己的编码规范,

第一步:Varint从末尾开始选取7位

序列化-Varint-算法-第一步-补1-补0.png

序列化-Varint-算法第二步-再次压缩.png

负数存储很麻烦

根据原始的数生成一个二进制,去做一个反转,反转以后再取一个异或运算,所以最后生成的结果会远远大于它本身。

按照这种存储,负数反而更大。

负数是(zigzag)

序列化-Varint-负数的序列化.png

Int 32 = -2 00000010

​ 11111101

​ 11111110

左移一位右边补0

右移31位,左边补1

111111100 ^ 11111111

00000011 (zigzag)

Protobuf存储

序列化-Protobuf存储-算法.png

Int 存的是32位数字

Tag、Length、Value

存储value时,

  1.  首位不懂
    
  2.  剩余位减1取反
    

Field number<<3 | wire type

Wire_type 0 int32 int64

Wire_type 1 18个字节 double

Thrift/Avro/Kryo/messagepack/FST

序列化选型的因素

  1.  序列化的开销,序列化结果大小,
    
  2.  序列化的性能,算法,计算规则,计算的耗时。
    
  3.  跨语言、跨平台、兼容性
    
  4.  学习成本,这门技术的学习成本。
    

二进制复杂对象的存储

序列化的性能分析地址:

微信公众号:不止极客

打开App,阅读手记
0人推荐
发表评论
随时随地看视频慕课网APP