Language Guide (proto3)&Google Protocol Buffer 的使用和原理
官方文档:https://developers.google.cn/protocol-buffers/docs/proto3
本来想自己翻译下 发现网上已经有很多比较优秀的翻译 就不班门弄斧了
https://www.cnblogs.com/tohxyblog/p/8974763.html
https://www.cnblogs.com/sanshengshui/p/9739521.html
https://cloud.tencent.com/developer/article/1504465
这里转载一个 https://www.ibm.com/developerworks/cn/linux/l-cn-gpb/index.html 关于原理的 稍微深入学习一下
用protoc来做一个本地的例子
目录结构如下
第一步:把addressbook.proto文件生成对应语言的结构库文件
// See README.txt for information and build instructions. // // Note: START and END tags are used in comments to define sections used in // tutorials. They are not part of the syntax for Protocol Buffers. // // To get an in-depth walkthrough of this file and the related examples, see: // https://developers.google.com/protocol-buffers/docs/tutorials // [START declaration] syntax = "proto3"; package pb; import "google/protobuf/timestamp.proto"; // [END declaration] // [START java_declaration] option java_package = "com.example.tutorial"; option java_outer_classname = "AddressBookProtos"; // [END java_declaration] // [START csharp_declaration] option csharp_namespace = "Google.Protobuf.Examples.AddressBook"; // [END csharp_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; google.protobuf.Timestamp last_updated = 5; } // Our address book file is just one of these. message AddressBook { repeated Person people = 1; } // [END messages]
1
protoc -I . --go_out=. addressbook.proto
编写writer add_person.go
package main import ( "bufio" "fmt" "io" "io/ioutil" "log" "os" "strings" "github.com/golang/protobuf/proto" "Dev/protobuffer/pb" ) func promptForAddress(r io.Reader) (*pb.Person, error) { // A protocol buffer can be created like any struct. p := &pb.Person{} rd := bufio.NewReader(r) fmt.Print("Enter person ID number: ") // An int32 field in the .proto file is represented as an int32 field // in the generated Go struct. if _, err := fmt.Fscanf(rd, "%d\n", &p.Id); err != nil { return p, err } fmt.Print("Enter name: ") name, err := rd.ReadString('\n') if err != nil { return p, err } // A string field in the .proto file results in a string field in Go. // We trim the whitespace because rd.ReadString includes the trailing // newline character in its output. p.Name = strings.TrimSpace(name) fmt.Print("Enter email address (blank for none): ") email, err := rd.ReadString('\n') if err != nil { return p, err } p.Email = strings.TrimSpace(email) for { fmt.Print("Enter a phone number (or leave blank to finish): ") phone, err := rd.ReadString('\n') if err != nil { return p, err } phone = strings.TrimSpace(phone) if phone == "" { break } // The PhoneNumber message type is nested within the Person // message in the .proto file. This results in a Go struct // named using the name of the parent prefixed to the name of // the nested message. Just as with pb.Person, it can be // created like any other struct. pn := &pb.Person_PhoneNumber{ Number: phone, } fmt.Print("Is this a mobile, home, or work phone? ") ptype, err := rd.ReadString('\n') if err != nil { return p, err } ptype = strings.TrimSpace(ptype) // A proto enum results in a Go constant for each enum value. switch ptype { case "mobile": pn.Type = pb.Person_MOBILE case "home": pn.Type = pb.Person_HOME case "work": pn.Type = pb.Person_WORK default: fmt.Printf("Unknown phone type %q. Using default.\n", ptype) } // A repeated proto field maps to a slice field in Go. We can // append to it like any other slice. p.Phones = append(p.Phones, pn) } return p, nil } // Main reads the entire address book from a file, adds one person based on // user input, then writes it back out to the same file. func main() { if len(os.Args) != 2 { log.Fatalf("Usage: %s ADDRESS_BOOK_FILE\n", os.Args[0]) } fname := os.Args[1] // Read the existing address book. in, err := ioutil.ReadFile(fname) if err != nil { if os.IsNotExist(err) { fmt.Printf("%s: File not found. Creating new file.\n", fname) } else { log.Fatalln("Error reading file:", err) } } // [START marshal_proto] book := &pb.AddressBook{} // [START_EXCLUDE] if err := proto.Unmarshal(in, book); err != nil { log.Fatalln("Failed to parse address book:", err) } // Add an address. addr, err := promptForAddress(os.Stdin) if err != nil { log.Fatalln("Error with address:", err) } book.People = append(book.People, addr) // [END_EXCLUDE] // Write the new address book back to disk. 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) } // [END marshal_proto] }
第二部编写reader list_people.go
package main import ( "fmt" "io" "io/ioutil" "log" "os" "github.com/golang/protobuf/proto" "Dev/protobuffer/pb" ) func writePerson(w io.Writer, p *pb.Person) { fmt.Fprintln(w, "Person ID:", p.Id) fmt.Fprintln(w, " Name:", p.Name) if p.Email != "" { fmt.Fprintln(w, " E-mail address:", p.Email) } for _, pn := range p.Phones { switch pn.Type { case pb.Person_MOBILE: fmt.Fprint(w, " Mobile phone #: ") case pb.Person_HOME: fmt.Fprint(w, " Home phone #: ") case pb.Person_WORK: fmt.Fprint(w, " Work phone #: ") } fmt.Fprintln(w, pn.Number) } } func listPeople(w io.Writer, book *pb.AddressBook) { for _, p := range book.People { writePerson(w, p) } } // Main reads the entire address book from a file and prints all the // information inside. func main() { if len(os.Args) != 2 { log.Fatalf("Usage: %s ADDRESS_BOOK_FILE\n", os.Args[0]) } fname := os.Args[1] // [START unmarshal_proto] // Read the existing address book. in, err := ioutil.ReadFile(fname) if err != nil { log.Fatalln("Error reading file:", err) } book := &pb.AddressBook{} if err := proto.Unmarshal(in, book); err != nil { log.Fatalln("Failed to parse address book:", err) } // [END unmarshal_proto] listPeople(os.Stdout, book) }
然后我们跑起来吧
go run add_person.go addressbook.data go run list_people.go addressbook.data
额外多说一句:为什么protobuffer 快呢 目前了解到
第一 序列化后生成的二进制非常紧凑
第二 封解包的速度很快 直接从二进制读取数据流并把解析出来的赋值相应的数据成员。