资讯专栏INFORMATION COLUMN

编解码技术

fobnn / 1402人阅读

摘要:基于提供的对象输入输出流和可以直接把对象作为可存储的字节数组写入文件也可以传输到网络上序列化的目的主要有两个网络传输对象持久化当进行远程跨进程服务调用时需要把被传输的对象编码为字节数组或者对象而当远程服务读取到对象或字节数组时需要将其解码为

基于 Java 提供的对象输入/输出流 ObjectInputStreamObjectOutputStream, 可以直接把 Java 对象作为可存储的字节数组写入文件, 也可以传输到网络上.

Java 序列化的目的主要有两个:

网络传输

对象持久化

当进行远程跨进程服务调用时, 需要把被传输的 Java 对象编码为字节数组或者 ByteBuffer 对象. 而当远程服务读取到 ByteBuffer 对象或字节数组时, 需要将其解码为发送时的 Java 对象. 这被称为 Java 对象编解码技术.

Java 序列化缺点

Java 序列化仅仅是 Java 编解码技术的一种, 由于它的种种缺陷, 衍生除了多种解码器技术和框架.

无法跨语言

对于跨进程的服务调用, 服务提供者可能会使用 C++ 或其他语言开发, 当我们需要和其他语言交互时, 由于 Java 序列化技术是 Java 语言内部的私有协议, 其他语言并不支持, 所以无法对其进行反序列化.

序列化后的码流太大

下面我们通过一个实例看下 Java 序列化后的字节数组大小.

public class UserInfo implements Serializable {

    private static final long serialVersionUID = 1L;

    private String userName;
    private int userID;

    public byte[] codeC() {
        ByteBuffer buffer = ByteBuffer.allocate(1024);
        byte[] value = this.userName.getBytes();
        buffer.put(value);
        buffer.putInt(this.userID);
        buffer.flip();
        value = null;
        byte[] result = new byte[buffer.remaining()];
        buffer.get(result);
        return result;
    }

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public int getUserID() {
        return userID;
    }

    public void setUserID(int userID) {
        this.userID = userID;
    }
}
public class App 
{
    public static void main( String[] args ) throws Exception {

        UserInfo userInfo = new UserInfo();
        userInfo.setUserID(100);
        userInfo.setUserName("Welcome to Netty");

        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
        objectOutputStream.writeObject(userInfo);
        objectOutputStream.flush();
        objectOutputStream.close();

        byte[] bytes = byteArrayOutputStream.toByteArray();

        System.out.println("The jdk serializable length is: " + bytes.length);
        System.out.println("The byte array serializable length is: " + userInfo.codeC().length);
    }
}

测试结果

The jdk serializable length is: 102
The byte array serializable length is: 20

测试结果令人震惊, 采用 JDK 序列化机制编码后的二进制数组大小尽然是二进制编码的 5.1 倍. 在同等情况下, 编码后的字节数组越大, 存储的时候就越占空间, 存储的硬件成本就越高, 并且在网络传输时更占带宽, 导致系统的吞吐量降低.

序列化性能太低

可以让创建代码循环 100 万次, 然后在前后加入获取系统时间.

业界主流的编解码框架 Google 的 Protobuf 介绍

Protobuf 全称 Google Protocole Buffers, 它由谷歌开源而来, 在谷歌内部久经考验. 它将数据结构以 .proto 文件进行描述, 通过代码生成工具可以生成对应数据结构的 POJO 对象和 Protobuf 相关的属性和方法.

它的特点如下.

结构化数据存储格式(XML JSON等);

高效的编解码性能;

语言无关、平台无关、扩展性好;

官方支持 Java、C++ 和 Python 三种语言.

为什么不使用 xml. 尽管 xml 的可读性和可扩展性非常好, 也非常适合描述数据结构, 但是 xml 解析的时间开销和 xml 为了可读性而牺牲的空间开销都非常大, 因此不适合做高性能的通信协议. Protobuf 使用二进制编码, 在空间和性能上具有更大的优势.

Protobuf 另一个比较吸引人的地方就是它的 数据描述文件和代码生成机制, 利用数据描述文件对数据结构进行说明的优点如下.

文本化的数据结构描述语言, 可以实现语言和平台无关, 特别适合异构系统间的集成.

通过标识字段的顺序, 可以实现协议的前向兼容;

自动代码生成, 不需要手工编写同样数据结构的 C++ 和 Java 版本;

方便后续的管理和维护. 相比于代码, 结构化的文档更容易管理和维护.

总结

我们判断一个编码器框架的优劣时, 往往会考虑以下几个因素.

是否支持跨语言, 支持的语言种类是否丰富;

编码后的码流大小;

编解码的性能;

类库是否小巧, API 使用是否方便;

使用者需要手工开发的工作量和难度.

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/72768.html

相关文章

  • GPU云服务器的应用场景

    摘要:现在我们通常聊到的云服务器指的是云服务器,这也是目前应用范围最广的云产品。但是我们也能看到云服务器存在的缺陷。这在一般用户看来是极高配置的云服务器在顶级用户看来只是将将满足需求。本文主要是聊聊云服务器的应用场景。 现在我们通常聊到的云服务器指的是ECS云服务器,这也是目前应用范围最广的云产品。ECS云服务器自出现开始,其具有的弹性伸缩、高可用性等特性满足了网站建设、企业应用运行、高峰流...

    dunizb 评论0 收藏0

发表评论

0条评论

fobnn

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<