序列化和反序列化的解释

826 阅读2分钟

1. 定义

后端服务都是基于微服务架构实现的。服务按照业务划分被拆分,实现了服务的解耦,但同时也带来了新的问题,不同业务之间通信需要通过接口实现调用。

两个服务之间要共享一个数据对象,就需要从对象转换成二进制流,通过网络传输,传送到对方服务,再转换回对象,供服务方法调用。这个编码和解码过程我们称之为序列化与反序列化

2. 衡量序列化框架好坏的标准

序列化的速度

序列化后的传输数据体积

在大量并发请求的情况下,如果序列化的速度慢,会导致请求响应时间增加;而序列化后的传输数据体积大,会导致网络吞吐量下降。所以一个优秀的序列化框架可以提高系统的整体性能。而目前主流的微服务框架却几乎没有用到 Java 序列化,SpringCloud 用的是 Json 序列化,Dubbo 虽然兼容了 Java 序列化,但默认使用的是 Hessian 序列化。

JDK 提供的两个输入、输出流对象 ObjectInputStream 和 ObjectOutputStream,它们只能对实现了 Serializable 接口的类的对象进行反序列化和序列化。

目前主流的微服务框架却几乎没有用到 Java 序列化,SpringCloud 用的是 Json 序列化,Dubbo 虽然兼容了 Java 序列化,但默认使用的是 Hessian 序列化, 最近几年比较流行的 FastJson、Kryo、Protobuf、Hessian 等。我们完全可以找一种替换掉 Java 序列化,这里我推荐使用 Protobuf 序列化框架。

3. Java 序列化的缺陷

无法跨语言, 如果是两个基于不同语言编写的应用程序相互通信,则无法实现两个应用服务之间传输对象的序列化与反序列化, C++ 写游戏服务,Java/Go 写周边服务,Python 写一些监控应用。

易被攻击

序列化后的流太大, Java 序列化中的编码耗时要比 ByteBuffer 长很多。

4. Java 序列化与 NIO 中的 ByteBuffer 编码的性能对比

public class SerializeTest {  
  
@Test  
public void test1() throws IOException {  
    UserBean user = new UserBean();  
    user.setUserName("test");  
    user.setPassword("test");  
    ByteArrayOutputStream os =new ByteArrayOutputStream();  
    ObjectOutputStream out = new ObjectOutputStream(os);  
    out.writeObject(user);  

    byte[] testByte = os.toByteArray();  
    System.out.print("ObjectOutputStream 字节编码长度:" + testByte.length + "\n");  


    ByteBuffer byteBuffer = ByteBuffer.allocate( 2048);  

    byte[] userName = user.getUserName().getBytes();  
    byte[] password = user.getPassword().getBytes();  
    byteBuffer.putInt(userName.length);  
    byteBuffer.put(userName);  
    byteBuffer.putInt(password.length);  
    byteBuffer.put(password);  

    byteBuffer.flip();  
    byte[] bytes = new byte[byteBuffer.remaining()];  
    System.out.print("ByteBuffer 字节编码长度:" + bytes.length+ "\n");  
  
}  
}
public class SerializeSpeedTest {  
  
    @Test  
    public void test1() throws IOException {  
        UserBean user = new UserBean();  
        user.setUserName("test");  
        user.setPassword("test");  

        long startTime = System.currentTimeMillis();  

        for(int i=0; i<1000; i++) {  
            ByteArrayOutputStream os =new ByteArrayOutputStream();  
            ObjectOutputStream out = new ObjectOutputStream(os);  
            out.writeObject(user);  
            out.flush();  
            out.close();  
            byte[] testByte = os.toByteArray();  
            os.close();  
        }  
        long endTime = System.currentTimeMillis();  
        System.out.print("ObjectOutputStream 序列化时间:" + (endTime - startTime) + "\n");  


        long startTime1 = System.currentTimeMillis();  
        for(int i=0; i<1000; i++) {  
            ByteBuffer byteBuffer = ByteBuffer.allocate( 2048);  

            byte[] userName = user.getUserName().getBytes();  
            byte[] password = user.getPassword().getBytes();  
            byteBuffer.putInt(userName.length);  
            byteBuffer.put(userName);  
            byteBuffer.putInt(password.length);  
            byteBuffer.put(password);  

            byteBuffer.flip();  
            byte[] bytes = new byte[byteBuffer.remaining()];  
        }  
        long endTime1 = System.currentTimeMillis();  
        System.out.print("ByteBuffer 序列化时间:" + (endTime1 - startTime1)+ "\n");  
  
}  
}