美文网首页
golang使用protobuf

golang使用protobuf

作者: StormZhu | 来源:发表于2019-02-07 18:45 被阅读0次

    简介

    http中常用的json协议一样,protobuf也是用来传输数据的,但是它使用二进制格式,传输效率更高。

    安装

    1. 下载protoc二进制程序
      下载链接
      在windows上,选择protoc-3.7.0-rc-2-win64.zip 进行下载。
      压缩包中有两个文件夹:
      压缩包中文件夹.png
      bin目录下的protoc.exe拷贝到GOPATH/bin目录下,将include/目录下的google文件夹拷贝GOPATH/src目录下(只有使用protobuf的一些内置结构才需要用到该文件夹内的文件,这次并不会用到这个文件夹)。
    2. 安装protobuf的编译器插件protoc-gen-go
      protoc程序会调用protoc-gen-go,将.proto文件生成golang代码。可以使用go get命令安装:
    go get -u -v github.com/golang/protobuf/protoc-gen-go
    

    安装成功后,会在GOPATH/bin下生成protoc-gen-go.exe程序。

    例子

    demo为官网tutorial的简化版本。

    在GOPATH/src/all-demo下,目录结构为:

    protobuf-demo
        demo1
            addressbook
                addressbook.pb.go
                addressbook.proto
            main.go
    

    编写、编译addressbook.proto文件

    其中addressbook.proto文件为:

    // [START declaration]
    syntax = "proto3";
    package addressbook;
    
    // [END declaration]
    
    // [START messages]
    message Person {
      string name = 1;
      int32 id = 2;  // Unique ID number for this person.
      string email = 3;
    
      enum PhoneType {
        MOBILE = 0;
        HOME = 1;
        WORK = 2;
      }
    
      message PhoneNumber {
        string number = 1;
        PhoneType type = 2;
      }
    
      repeated PhoneNumber phones = 4;
    }
    
    // Our address book file is just one of these.
    message AddressBook {
      repeated Person people = 1;
    }
    // [END messages]
    

    其中:

    1. syntax设置语法类型,有proto2proto3两种语法。
    2. package addressbook可以设置生成的golang代码的包名。
    3. message对应于golang中的struct,可以看到文件中一共定义了PersonPhoneNumberAddressBook3个message,其中PhoneNumberPerson的嵌套类型。
    4. message中有字段,可以是intstring,枚举或者其他消息类型。
    5. repeated表示该字段可以不止一个,类似于golang中的slice

    编译

    命令行进入GOPATH/src/all-demo/protobuf-demo/demo1/addressbook,
    执行protoc --go_out=. addressbook.proto,会在该目录下生成addressbook.pb.go文件。
    其中 --go_out指定生成的golang文件的目录。

    addressbook.pb.go部分源码如下:

    // 可以看到包名为addressbook
    package addressbook
    // [START messages]
    type Person struct {
        Name                 string                `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
        Id                   int32                 `protobuf:"varint,2,opt,name=id,proto3" json:"id,omitempty"`
        Email                string                `protobuf:"bytes,3,opt,name=email,proto3" json:"email,omitempty"`
        Phones               []*Person_PhoneNumber `protobuf:"bytes,4,rep,name=phones,proto3" json:"phones,omitempty"`
        XXX_NoUnkeyedLiteral struct{}              `json:"-"`
        XXX_unrecognized     []byte                `json:"-"`
        XXX_sizecache        int32                 `json:"-"`
    }
    type Person_PhoneNumber struct {
        Number               string           `protobuf:"bytes,1,opt,name=number,proto3" json:"number,omitempty"`
        Type                 Person_PhoneType `protobuf:"varint,2,opt,name=type,proto3,enum=addressbook.Person_PhoneType" json:"type,omitempty"`
        XXX_NoUnkeyedLiteral struct{}         `json:"-"`
        XXX_unrecognized     []byte           `json:"-"`
        XXX_sizecache        int32            `json:"-"`
    }
    // Our address book file is just one of these.
    type AddressBook struct {
        People               []*Person `protobuf:"bytes,1,rep,name=people,proto3" json:"people,omitempty"`
        XXX_NoUnkeyedLiteral struct{}  `json:"-"`
        XXX_unrecognized     []byte    `json:"-"`
        XXX_sizecache        int32     `json:"-"`
    }
    

    编写main.go

    代码主要测试proto.Marshalproto.UnMarshal的功能。先定义一个pb.AddressBook 结构体,并将其初始化,用proto.Marshal将其序列化成二进制数据,写入文件,再将其从文件中读取,使用proto.UnMarshal反序列化成结构体。
    代码如下:

    package main
    
    import (
        "fmt"
        pb "all-demo/protobuf-demo/demo1/addressbook"
        "io/ioutil"
        "log"
        "github.com/golang/protobuf/proto"
    )
    
    func main() {
        // 自定义AddressBook内容
        book := &pb.AddressBook{
            People: []*pb.Person {
                &pb.Person{
                    Id: 1,
                    Name: "zyq",
                    Email: "77@qq.com",
                    Phones: []*pb.Person_PhoneNumber{
                        &pb.Person_PhoneNumber {
                            Number: "11111",
                            Type: pb.Person_MOBILE,
                        },
                        &pb.Person_PhoneNumber {
                            Number: "22222",
                            Type: pb.Person_HOME,
                        },
                    },
                },
            },
        }
        fmt.Println("book : ",book)
    
        fname := "address.dat"
        // 将book进行序列化
        out, err := proto.Marshal(book)
        if err != nil {
            log.Fatalln("Failed to encode address book:", err)
        }
        // 将序列化的内容写入文件
        if err := ioutil.WriteFile(fname, out, 0644); err != nil {
            log.Fatalln("Failed to write address book:", err)
        }
    
        // 读取写入的二进制数据
        in, err := ioutil.ReadFile(fname)
        if err != nil {
            log.Fatalln("Error reading file:", err)
        }
    
        // 定义一个空的结构体
        book2 := &pb.AddressBook{}
        // 将从文件中读取的二进制进行反序列化
        if err := proto.Unmarshal(in, book2); err != nil {
            log.Fatalln("Failed to parse address book:", err)
        }
    
        fmt.Println("book2: ",book2)
    }
    

    执行结果为:

    book :  people:<name:"zyq" id:1 email:"77@qq.com" phones:<number:"11111" > phones:<number:"22222" type:HOME > > 
    book2:  people:<name:"zyq" id:1 email:"77@qq.com" phones:<number:"11111" > phones:<number:"22222" type:HOME > > 
    

    问题:

    • 还有很多语法不了解。
    • proto.UnMarshal函数的声明为func Unmarshal(buf []byte, pb Message) error,该函数的第二个参数为proto.Message接口,在网络传输中,服务端怎么知道客户端发过来的到底是哪一种message

    参考

    1. https://developers.google.com/protocol-buffers/docs/gotutorial

    相关文章

      网友评论

          本文标题:golang使用protobuf

          本文链接:https://www.haomeiwen.com/subject/exkgsqtx.html