GRPC学习之路(3)——protobuf的作用 - Go语言中文社区

GRPC学习之路(3)——protobuf的作用


上一篇文章介绍的是nginx做为负载均衡与后端grpc的集成,没有继续深入下去,因为这个需要线上真正实践才会有更多的感悟,之后入职后有体会的话再继续写吧。

刚好我也比较好奇protobuf到底和grpc是个什么关系,protobuf在整个rpc的过程中起的是什么作用,所以我之后的几篇文章都会是关于protobuf的。

我还是从网上找了几个网上的资料,看看前人的研究成果:

proto3官网 毫不犹豫推荐官网的文章

Google Protocol Buffer 的使用和原理 这个文章有点老,介绍的不是proto3,但是作者研究的挺深入的。

proto3语言指南 这个有点像官网英文的翻译和作者自己的总结,也非常不错

什么是ProtoBuf

摘抄一段别人的描述:Protocol Buffers 是一种轻便高效的结构化数据存储格式,可以用于结构化数据串行化,或者说序列化。它很适合做数据存储或 RPC 数据交换格式。可用于通讯协议、数据存储等领域的语言无关、平台无关、可扩展的序列化结构数据格式。看到这个描述第一感觉是云里雾里,研究后总结protobuf的以下几点:

  • grpc作为rpc的一种,肯定有服务间的网络调用,调用就一定会有数据的传输,而这个数据的传输就用的是protobuf去序列化和反序列化的,一个请求的内容在调用方序列化通过网络传送到服务方,服务方就能用protobuf反序列化出来,后面的文章会讲解细节。
  • protobuf有一个类似thrift的IDL(接口描述语言),就是描述一个对象包括什么字段,一个服务包括什么接口,简单如下:

message Person {
string name = 1;
int32 id = 2;
string email = 3;
}

  • protobuf还有专门的工具生成对象代码,你用这些代码就能将对象序列化到文件或者流中,当然也能反序列化成对象,后面的例子会具体介绍。
  • protobuf支持多种语言,常见的比如go,c++,java,php等,具体可以参考官网

怎么使用ProtoBuf

准备工作

与前面介绍grpc使用的步骤一样,现在准备一个.proto文件

syntax = "proto3";

package tutorial;

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

message Person {
  string name = 1;
  int32 id = 2;
  string email = 3;

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

  message PhoneNumber {
    string number = 1;
    PhoneType type = 2;
  }

  repeated PhoneNumber phones = 4;
}

message AddressBook {
  repeated Person people = 1;
}

使用前面文章介绍的maven plugin生成代码。

拷贝代码

将生成好的代码AddressBookProtos.java拷贝到你新建的工程,类似如下结构:

序列化对象

参考官网的说明,新建一个WriteObject.java, 代码如下,通过输入逐渐构造Person对象,并写入一个文件,记得在启动参数里加上文件的路径

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();
}

public static void main(String[] args) throws IOException {
    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(CodedInputStream.newInstance(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();
}

运行后用sublime打开写入的文件如下, 具体为什么是这种后面我会具体研究,现在所要知道的是这种序列化的方式肯定比一般的xml和json要节省空间的

反序列化对象

参考官网新建ReadObject.java, 代码如下, 读取同样的文件,反序列化后就能取出对应的字段值

static void Print(AddressBook addressBook) {
    for (Person person: addressBook.getPeopleList()) {
        System.out.println("Person ID: " + person.getId());
        System.out.println("  Name: " + person.getName());

        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());
        }
    }
}

public static void main(String[] args) throws IOException {
    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);
}

运行一下结果如下:

Person ID: 1
Name: zack

总结

至此,大概了解了一下怎么单独使用protobuf以及它在grpc中的作用,后面我会继续研究protobuf3的语法细节和序列化细节

另附文章中提到的工程文件:

Proto3Tutorial

欢迎关注我的个人的博客www.zhijianliu.cn, 虚心求教,有错误还请指正轻拍,谢谢

版权声明:本文出自志健的原创文章,未经博主允许不得转载

版权声明:本文来源CSDN,感谢博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/toxie6415176/article/details/80244002
站方申明:本站部分内容来自社区用户分享,若涉及侵权,请联系站方删除。
  • 发表于 2020-03-08 10:43:52
  • 阅读 ( 1899 )
  • 分类:

0 条评论

请先 登录 后评论

官方社群

GO教程

猜你喜欢