Protobuf介绍以及简单使用

举报
i进击的攻城狮 发表于 2022/06/08 23:07:33 2022/06/08
【摘要】 一、什么是Protobuf公司的项目用到了一门技术,之前也没听说过,了解得知,Protobuf是谷歌出的一门技术,用于数据传输。我们知道,传统的数据传输格式,有JSON,XML,这些都不陌生,结合JSON 和 XML 来理解,Protobuf和他们是同一类型的事物,它有它的优点。Protobuf 对比于他们两个,拥有着体量更小,解析速度更快的优势,所以,在 IM 这种通信应用中,非常适合将...

一、什么是Protobuf

公司的项目用到了一门技术,之前也没听说过,了解得知,Protobuf是谷歌出的一门技术,用于数据传输。
我们知道,传统的数据传输格式,有JSON,XML,这些都不陌生,结合JSON 和 XML 来理解,Protobuf和他们是同一类型的事物,它有它的优点。
Protobuf 对比于他们两个,拥有着体量更小,解析速度更快的优势,所以,在 IM 这种通信应用中,非常适合将 Protobuf 作为数据传输格式。

image.png

二、对比JSON和XML

image.png

三、protobuf的使用

3.1 编写proto文件

钥使用proto,首先需要编写proto文件,这个文件用于定义数据的类型,定义这次数据传输过程中,数据有哪些字段。
后缀名是.proto,如下:

syntax = "proto3"; // PB协议版本

import "google/protobuf/any.proto"; // 引用外部的message,可以是本地的,也可以是此处比较特殊的 Any

package com.protobuf; // 包名,其他 proto 在引用此 proto 的时候,就可以使用 test.protobuf.PersonTest 来使用,
// 注意:和下面的 java_package 是两种易混淆概念,同时定义的时候,java_package 具有较高的优先级

option java_package = "com.jet.protobuf"; // 生成类的包名,注意:会在指定路径下按照该包名的定义来生成文件夹
option java_outer_classname="UserProtos"; // 生成类的类名,注意:下划线的命名会在编译的时候被自动改为驼峰命名
option java_multiple_files = true;

message User {
  int32  id = 1; // int 类型
  string name = 2; // string 类型
  Sex sex = 3; // 枚举类型
  repeated PhoneNumber phone = 4; // 引用下面定义的 PhoneNumber 类型的 message
  map<string, string> tags = 5; // map 类型
  repeated google.protobuf.Any details = 6; // 使用 google 的 any 类型

  // 定义一个枚举
  enum Sex {
    DEFAULT = 0;
    MALE = 1;
    Female = 2;
  }

  // 定义一个 message
  message PhoneNumber {
    string number = 1;
    PhoneType type = 2;

    enum PhoneType {
      MOBILE = 0;
      HOME = 1;
      WORK = 2;
    }

  }

}

3.2编译

protoc -I=./ --java_out=./ ./user.proto
或
protoc -proto_path=./ --java_out=./ ./user.proto

编译后生成如下文件,这些文件如何使用下文会介绍。
image.png

3.3 语法介绍

每个值后面都有序号,从1开始,枚举从0开始。
repeated 表示该字段是可以重复的,这种重复实际上就是一种数组的结构。
java_multiple_files, java_package, 和 java_outer_classname是专门个Java准备的设置。

其中java_multiple_files指编译过后java文件的个数,如果是true,那么将会一个java对象一个类,如果是false,那么定义的java对象将会被包含在同一个文件中。

java_package指定生成的类应该使用的Java包名称。 如果没有明确的指定,则会使用之前定义的package的值。

java_outer_classname选项定义将表示此文件的包装类的类名。 如果没有给java_outer_classname赋值,它将通过将文件名转换为大写驼峰来生成。 例如,默认情况下,“user.proto”将使用"User"作为包装类名称。

3.4变量类型

对于简单类型来说可以使用bool, int32, float, double, 和 string来定义字段的类型。各个语言的数据类型和proto对应关系如下。
image.png

四、使用proto

之前不是生成了3个文件吗,使用的demo如下。


public class ProtoTest {

    public static void main(String[] args) {
        try {
            /** Step1:生成 userBuilder 对象 */
            //  构造器
            User.Builder userBulider = User.newBuilder();
            // 构造器 赋值
            userBulider.setId(1);
            userBulider.setName("zhangsna");
            userBulider.setSex(User.Sex.Female);

            User.PhoneNumber.Builder phoneNumber = User.PhoneNumber.newBuilder();
            phoneNumber.setNumber("13131313311");
            phoneNumber.setType(User.PhoneNumber.PhoneType.HOME);

            userBulider.addPhone(phoneNumber);


            userBulider.putTags("key","value");

            userBulider.addDetails(Any.newBuilder().build());

            //构造出类
            User user = userBulider.build();



            /** Step2:序列化和反序列化 */
            // 方式一 byte[]:
            // 序列化
            byte[] bytes = user.toByteArray();
            // 反序列化
            User userTestResult = User.parseFrom(bytes);
            System.out.println(String.format("反序列化得到的信息,姓名:%s,性别:%d,手机号:%s", userTestResult.getName(), userTestResult.getSexValue(), userTestResult.getPhone(0).getNumber()));



            // 方式二 ByteString:
            // 序列化
            ByteString byteString = user.toByteString();
            System.out.println(byteString.toString());
            // 反序列化
            User userTestResult2 = User.parseFrom(byteString);
            System.out.println(String.format("反序列化得到的信息,姓名:%s,性别:%d,手机号:%s", userTestResult2.getName(), userTestResult2.getSexValue(), userTestResult2.getPhone(0).getNumber()));



            // 方式三 InputStream
            // 粘包,将一个或者多个protobuf 对象字节写入 stream
            // 序列化
            ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
            user.writeDelimitedTo(byteArrayOutputStream);
            // 反序列化,从 steam 中读取一个或者多个 protobuf 字节对象
            ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
            User personTestResult3 = User.parseDelimitedFrom(byteArrayInputStream);
            System.out.println(String.format("反序列化得到的信息,姓名:%s,性别:%d,手机号:%s", personTestResult3.getName(), personTestResult3.getSexValue(), personTestResult3.getPhone(0).getNumber()));

        } catch (InvalidProtocolBufferException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

}

五、总结

XML、JSON、Protobuf 都具有数据结构化和数据序列化的能力,但使用方法不同,proto通过proto文件去构造对象。可以将这个对象生成字节数组,或者流,在网络中传输。

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。