Google protobuf


概述

  • Protocol Buffers是一种序列化数据结构的方法,Protocol Buffers经常被简称为protobuf。
  • 它不依赖于语言和平台并且可扩展性极强。google protobuf是跨语言的,并且自带了一个编译器(protoc),只需要用它进行编译,可以编译成Java、python、C++、C#、Go等代码,然后就可以直接使用,不需要再写其他代码,自带有解析的代码。更详细的介绍见: [Protocol Buffers]

通过它,你可以定义你的数据的结构,并生成基于各种语言的代码。这些你定义的数据流可以轻松地在传递并不破坏你已有的程序。并且你也可以更新这些数据而现有的程序也不会受到任何的影响。


在使用之前,需要下载2个包:

  1. protobuf (protobuf 在你应用中使用的api包)。protobuf-3.3.0下载链接。
  2. protoc(编译器,windows版内涵protoc.exe, 用来把proto文件编译成目标语言(C++,Java,Python)的文本。比如目标语言java则声称xxx.java文件)。protoc-3.3.0-win32下载链接

Protocol Buffer Basics: Java

本文主要介绍下在java中使用Protocol Buffer, 通过一个简单的例子应用,将介绍如下内容:

  • 如何在 a .proto文件中定义消息格式
  • 如何使用 protocol buffer编译器
  • 使用 Java protocol buffer API 来读写消息

如果想要理解更多可以参考Protocol Buffer Language GuideJava API ReferenceJava Generated Code GuideEncoding Reference。

Why Use Protocol Buffers?

“通讯录”应用程序是一个非常简单的例子,它可以读取和写入人们的联系信息。 地址簿中的每个人都有姓名,身份证件,电子邮件地址和联系电话号码。

-如何序列化和检索这样的结构化数据? 有几种方法来解决这个问题:

  • 使用Java序列化。 这是默认的方法,并且因为它是java语言内置的,如果你需要以C ++或Python编写的应用程序,那么共享数据也不会很好 。
  • 您可以发明一种将数据项编码为单个字符串的ad-hoc方式,例如将4个ints编码为“12:3:-23:67”。 这是一个简单而灵活的方法,尽管它需要编写一次性编码和解析代码,但解析运行时成本很小。 这最适合编码非常简单的数据。
  • 将数据序列化为XML。

而Protocol Buffers是灵活,高效,自动化的来解决这个问题的解决方案。 使用Protocol Buffers您可以编写一个.proto描述您希望存储的数据结构。 从那里,Protocol Buffers编译器创建一个实现自动编码和解析Protocol Buffers数据的类,并使用高效的二进制格式。 生成的类为组成Protocol Buffers的字段提供getter和setter,并且将单元中的协议缓冲区的读取和写入细节。 重要的是,Protocol Buffers格式支持延长格式随时间推移的想法,使得代码仍然可以读取以旧格式编码的数据。


Defining Your Protocol Format

使用Protocol Buffers 首先需要定义一个后缀名为proto的文件(addressbook.proto)。

.proto文件中的定义很简单:您要为要序列化的每个数据结构添加消息,然后为消息中的每个字段指定名称和类型。 这是定义您的消息的.proto文件,addressbook.proto。

addressbook.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;
}
  • package tutorial :.proto文件 首先定义了一个包名(tutorial)主要用来避免不同project之间包命名冲突的问题。在java中如果没有定义java_package,就以package(这里tutorial)作为包名。

  • java_package :生成java类的包名,如果生成其他类文件,则忽略次项。

  • message:定义消息。上文的addressbook.proto文件,定义了3个消息,Person 、PhoneNumber 、AddressBook。

  • required :表示消息中,这个参数是必须的,optional 表参数可选。repeated,表该属性可以重复多少次数(包括零),将重复的字段视为动态大小的数组。

完整的.proto文件语法,参考Protocol Buffer Language Guide


Compiling Your Protocol Buffers

  1. 下载编译器,前面已经讲到。
  2. 执行编译需要指定源文件目录,和生成文件目录,这个好理解。由 SRCDIR DST_DIR表示输出文件目录(也就是要生成的java类文件存放目录)。如果不指定,将会默认问当前路径。
protoc -I=$SRC_DIR --java_out=$DST_DIR $SRC_DIR/addressbook.proto

这里的编译示例如下:

  1. 解压下载的protoc-3.3.0-win32.zip,假设解压后目录 ${PROTO_HOME}。
  2. 打开命令行工具,cd /d ${PROTO_HOME}/bin
  3. 执行protoc -I=$SRC_DIR --java_out=$DST_DIR $SRC_DIR/addressbook.proto 。假设$SRC_DIR目录为 D:/SRC_DIR,并且addressbook.proto文件放置在该目录下 。$DST_DIR 设定为D:\DST_DIR。执行完成,可在$DST_DIR目录下看到AddressBookProtos.java文件。

Writing A Message

import com.example.tutorial.AddressBookProtos.AddressBook;
import com.example.tutorial.AddressBookProtos.Person;
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.InputStreamReader;
import java.io.IOException;
import java.io.PrintStream;

class AddPerson {
  // This function fills in a Person message based on user input.
  static Person PromptForAddress(BufferedReader stdin,
                                 PrintStream stdout) throws IOException {
    Person.Builder person = Person.newBuilder();

    stdout.print("Enter person ID: ");
    person.setId(Integer.valueOf(stdin.readLine()));

    stdout.print("Enter name: ");
    person.setName(stdin.readLine());

    stdout.print("Enter email address (blank for none): ");
    String email = stdin.readLine();
    if (email.length() > 0) {
      person.setEmail(email);
    }

    while (true) {
      stdout.print("Enter a phone number (or leave blank to finish): ");
      String number = stdin.readLine();
      if (number.length() == 0) {
        break;
      }

      Person.PhoneNumber.Builder phoneNumber =
        Person.PhoneNumber.newBuilder().setNumber(number);

      stdout.print("Is this a mobile, home, or work phone? ");
      String type = stdin.readLine();
      if (type.equals("mobile")) {
        phoneNumber.setType(Person.PhoneType.MOBILE);
      } else if (type.equals("home")) {
        phoneNumber.setType(Person.PhoneType.HOME);
      } else if (type.equals("work")) {
        phoneNumber.setType(Person.PhoneType.WORK);
      } else {
        stdout.println("Unknown phone type.  Using default.");
      }

      person.addPhones(phoneNumber);
    }

    return person.build();
  }

  // Main function:  Reads the entire address book from a file,
  //   adds one person based on user input, then writes it back out to the same
  //   file.
  public static void main(String[] args) throws Exception {
    if (args.length != 1) {
      System.err.println("Usage:  AddPerson ADDRESS_BOOK_FILE");
      System.exit(-1);
    }

    AddressBook.Builder addressBook = AddressBook.newBuilder();

    // Read the existing address book.
    try {
      addressBook.mergeFrom(new FileInputStream(args[0]));
    } catch (FileNotFoundException e) {
      System.out.println(args[0] + ": File not found.  Creating a new file.");
    }

    // Add an address.
    addressBook.addPeople(
      PromptForAddress(new BufferedReader(new InputStreamReader(System.in)),
                       System.out));

    // Write the new address book back to disk.
    FileOutputStream output = new FileOutputStream(args[0]);
    addressBook.build().writeTo(output);
    output.close();
  }
}

Reading A Message

import com.example.tutorial.AddressBookProtos.AddressBook;
import com.example.tutorial.AddressBookProtos.Person;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.PrintStream;

class ListPeople {
  // Iterates though all people in the AddressBook and prints info about them.
  static void Print(AddressBook addressBook) {
    for (Person person: addressBook.getPeopleList()) {
      System.out.println("Person ID: " + person.getId());
      System.out.println("  Name: " + person.getName());
      if (person.hasEmail()) {
        System.out.println("  E-mail address: " + person.getEmail());
      }

      for (Person.PhoneNumber phoneNumber : person.getPhonesList()) {
        switch (phoneNumber.getType()) {
          case MOBILE:
            System.out.print("  Mobile phone #: ");
            break;
          case HOME:
            System.out.print("  Home phone #: ");
            break;
          case WORK:
            System.out.print("  Work phone #: ");
            break;
        }
        System.out.println(phoneNumber.getNumber());
      }
    }
  }

  // Main function:  Reads the entire address book from a file and prints all
  //   the information inside.
  public static void main(String[] args) throws Exception {
    if (args.length != 1) {
      System.err.println("Usage:  ListPeople ADDRESS_BOOK_FILE");
      System.exit(-1);
    }

    // Read the existing address book.
    AddressBook addressBook =
      AddressBook.parseFrom(new FileInputStream(args[0]));

    Print(addressBook);
  }
}

高级用法

Protocol buffers的用法远不止简单的加速和序列化。在 Java API reference可以了解更多信息。

参考
  1. protobuf安装及C++版实现示例。
    http://www.cnblogs.com/luoxn28/p/5303517.html
  2. Protobuf简单使用。
    http://www.iloveandroid.net/2015/10/08/studyPtorobuf/
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值