Protobuf入门

👨‍💻 导读:“

Google Protocol Buffer( 简称 Protobuf) 是 Google 公司内部的混合语言数据标准 ,是一种轻便高效的结构化数据存储格式,可以用于结构化数据串行化,或者说序列化(将 数据结构或对象 转换成 二进制串 的过程 )。它很适合做数据存储RPC 数据交换格式。可用于通讯协议、数据存储等领域的语言无关、平台无关、可扩展的序列化结构数据格式

protocol buffers 诞生之初是为了解决服务器端新旧协议(高低版本)兼容性问题,名字也很体贴,“协议缓冲区”。只不过后期慢慢发展成用于传输数据。

笔者所在的360广告投放,N亿条商品信息的数据全部采用PB格式存储、传输。

Protobuf 的优点

  • 更小——序列化后,数据大小可缩小约3倍

  • 更快——序列化速度更快,比xml和JSON快20-100倍,体积缩小后,传输时,带宽也会优化

  • 更简单——proto编译器,自动进行序列化和反序列化

  • 维护成本低——跨平台、跨语言,多平台仅需要维护一套对象协议(.proto)

  • 可扩展——“向后”兼容性好,不必破坏已部署的、依靠“老”数据格式的程序就可以对数据结构进行升级

  • 加密性好——HTTP传输内容抓包只能看到字节

    在传输数据量大、网络环境不稳定的数据存储和RPC数据交换场景比较合适

Protobuf 的不足

  • 功能简单,无法用来表示复杂的概念
  • 通用性较差,XML和JSON已成为多种行业标准的编写工具,pb只是geogle内部使用
  • 自解释性差,以二进制数据流方式存储(不可读),需要通过.proto文件才可以

官网 Protocol Buffer Basics: Java https://developers.google.com/protocol-buffers/docs/javatutorial

Hello World

1. 定义 .proto 文件的消息格式(你希望存储的数据格式描述文件)
syntax = "proto2";

package tutorial;

option java_package = "com.example.tutorial";
option java_outer_classname = "AddressBookProtos";

//消息模型
message Person {
//消息对象的字段:字段修饰符+字段类型+字段名称+标识号(通过二进制格式唯一标识每个字段,不变可)
  required string name = 1;
  required int32 id = 2;
  optional string email = 3;

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

  message PhoneNumber {
    required string number = 1;
    optional PhoneType type = 2 [default = HOME];
  }

  repeated PhoneNumber phones = 4;
}

message AddressBook {
  repeated Person people = 1;
}

☆☆☆注:

  • syntax = "proto2":指明版本

  • package:PB的自己的包名,防止不同 .proto 项目间命名 发生冲突

  • java_package: 生成java类的包名,如不显式指定,默认包名为:按照应用名称倒序方式进行排序

  • java_outer_classname:生成 java类的类名,如不显式指定,则默认为把.proto文件名转换为首字母大写来生成

  • message: 你的消息格式,各数据类型(bool, int32, float, double, stringenum ... )字段的集合,在一个.proto文件中可以定义多个message,一个message里也可以定义另外一个message(相当于java的类,当然也可以有内部类)

  • 当然PB也是支持和java一样的import的,import "xxx.proto";

  • 像每个字段也必须有修饰符,PB提供的字段修饰符有3种

    • required:必填
    • optional:可选
    • repeated :可重复字段,可放集合
  • 标识号:通过二进制格式唯一标识每个字段 ,使用后就不能够再改变

    • 标识号使用范围:[1,2的29次方 - 1]

    • 不可使用 [19000-19999] 标识号, 因为 Protobuf 协议实现中对这些标识号进行了预留。假若使用,则会报错

    • 每个字段在进行编码时都会占用内存,而 占用内存大小 取决于 标识号:

      • 范围 [1,15] 标识号的字段 在编码时占用1个字节;
      • 范围 [16,2047] 标识号的字段 在编码时占用2个字节
      • 为频繁出现的 消息字段 保留 [1,15] 的标识号
image
2. 使用 protocol buffer 编译器(下载地址:https://github.com/protocolbuffers/protobuf/releases )
winows的话 cmd到编译器安装目录的bin目录中,执行 protoc.exe -h (E:\learning\protoc-3.9.0-win64\bin>protoc.exe -h),可以看到参数说明。

执行:protoc -I=源地址 --java_out=目标地址 源地址/xxx.proto

E:\learning\protoc-3.9.0-win64\bin>protoc.exe -I=E:\learning\ --java_out=E:\lear ning\ E:\learning\addressbook.proto

实际使用中:

protoc.exe -I=E:\learn-workspace\starfish\starfish-learn\src\main\java\priv\starfish\ProtocolBuffers\proto\ --java_out=E:\learn-workspace\starfish\starfish-learn\src\main\java E:\learn-workspace\starfish\starfish-learn\src\main\java\priv\starfish\ProtocolBuffers\proto\addressbook.proto)

pd-idea-screenshot

<figcaption style="margin-top: 5px; text-align: center; color: #888; display: block; font-size: 12px; font-family: PingFangSC-Light;">pd-idea-screenshot</figcaption>

3. 通过 Java protocol buffer API 读写消息格式
package priv.starfish.ProtocolBuffers;
import com.google.protobuf.InvalidProtocolBufferException;
import priv.starfish.ProtocolBuffers.AddressBookProtos.Person;
import priv.starfish.ProtocolBuffers.AddressBookProtos.AddressBook;
import java.util.Arrays;
/**
 * @author: starfish
 * @date: 2019/7/24 14:39
 * @description:
 */
public class HelloProto {
    public static void main(String[] args) {
        Person person = Person.newBuilder()
                .setId(123)
                .setName("starfish")
                .setEmail("starfish@126.cn")
                .addPhones(AddressBookProtos.Person.PhoneNumber.newBuilder()
                        .setType(AddressBookProtos.Person.PhoneType.HOME)
                        .setNumber("13555555555")
                        .build())
                .build();

        System.out.println(person.toString());
        System.out.println(person.isInitialized());

        try {
            //序列化和反序列化
            System.out.println(Arrays.toString(person.toByteArray()));
            System.out.println(person.toByteString());
            Person newPerson = Person.parseFrom(person.toByteArray());
            System.out.println(newPerson);
            newPerson = Person.parseFrom(person.toByteString());
            System.out.println(newPerson);
        } catch (InvalidProtocolBufferException e) {
            e.printStackTrace();
        }

        // 向地址簿添加两条Person信息
        AddressBook.Builder books = AddressBook.newBuilder();
        books.addPeople(person);
        books.addPeople(Person.newBuilder(person).setEmail("xin@163.com")
                .build());
        System.out.println("AddressBook对象信息:");
        System.out.println(books.build());
    }
}

编译后生成的java类是不可变的,类似java的String,不可修改

构造消息,必须先构造一个builder,然后set属性(可以一连串的set),最后调用build() 方法。

PB常用方法

  • isInitialized(): 检查必填字段(required)是否有set值

  • toString(): 返回message的可读字符串格式

  • mergeFrom(Message other): 合并message

  • clear(): 清空字段值

  • byte[] toByteArray();: 序列化message,返回字节数组

  • MessageType parseFrom(byte[] data);: 解析给定的字节数组

  • void writeTo(OutputStream output);: 序列化message并写入输出流OutputStream.

  • MessageType parseFrom(InputStream input);: 从输入流 InputStream读取并解析message

Reference: https://github.com/halfrost/Halfrost-Field/blob/master/contents/Protocol/Protocol-buffers-encode.md

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,185评论 6 503
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,652评论 3 393
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,524评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,339评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,387评论 6 391
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,287评论 1 301
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,130评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,985评论 0 275
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,420评论 1 313
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,617评论 3 334
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,779评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,477评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,088评论 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,716评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,857评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,876评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,700评论 2 354

推荐阅读更多精彩内容