golang基础-protobuf使用

前些日子了解过python中protobuf,今天主要是来学习下如何使用golang来使用protobuf

python基础–protobuf的使用(一)
python基础—protobuf的使用(还未完成)

参考资料:
https://github.com/google/protobuf

以下的需要VPN翻墙
pythontutorial
https://developers.google.com/protocol-buffers/docs/pythontutorial

python-generated
https://developers.google.com/protocol-buffers/docs/reference/python-generated#extension

语言指南
https://developers.google.com/protocol-buffers/docs/proto3
API文档
https://developers.google.com/protocol-buffers/docs/reference/python/?hl=zh-cn

Mac安装

Protobuf是Protocol Buffers的简称,它是Google公司开发的一种数据描述语言,并于2008年对外开源。Protobuf刚开源时的定位类似于XML、JSON等数据描述语言,通过附带工具生成代码并实现将结构化数据序列化的功能。但是我们更关注的是Protobuf作为接口规范的描述语言,可以作为设计安全的跨语言PRC接口的基础工具。

Protobuf核心的工具集是C++语言开发的,在官方的protoc编译器中并不支持Go语言。要想基于上面的hello.proto文件生成相应的Go代码,需要安装相应的插件。首先是安装官方的protoc工具

Mac通过brew install protobuf安装即可
然后是安装针对Go语言的代码生成插件,可以通过go get github.com/golang/protobuf/protoc-gen-go命令安装
然后通过以下命令生成相应的Go代码:protoc ./hello.proto --go_out=./

摘自谷歌protobuf官网案例

参考链接https://github.com/iyongfei/protobuf/tree/master/examples
链接包含java、python、go等多种语言来实现的protobuf的例子,我今天就把go语言中的例子摘出来
首先看我的项目目录结构:
这里写图片描述

首先看下dressbook.proto

syntax = "proto3";
package go_protoc;

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

然后通过 protoc ./dressbook.proto --go_out=./ 进行编译

zhiliaodeMBP:go_protoc zhiliao$ protoc ./dressbook.proto --go_out=./
zhiliaodeMBP:go_protoc zhiliao$ pwd
/Users/zhiliao/zhiliao/go/src/go_protoc
zhiliaodeMBP:go_protoc zhiliao$ 

我们来看下添加的功能add_person.go

package main

import (
    "bufio"
    "fmt"
    "go_protoc"
    "io"
    "io/ioutil"
    "log"
    "os"
    "strings"
    "github.com/golang/protobuf/proto"
)

func promptForAddress(r io.Reader) (*go_protoc.Person, error) {
    p := &go_protoc.Person{}

    rd := bufio.NewReader(r)
    fmt.Print("Enter person ID number: ")
    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
    }
    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
        }
        pn := &go_protoc.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)

        switch ptype {
        case "mobile":
            pn.Type = go_protoc.Person_MOBILE
        case "home":
            pn.Type = go_protoc.Person_HOME
        case "work":
            pn.Type = go_protoc.Person_WORK
        default:
            fmt.Printf("Unknown phone type %q.  Using default.\n", ptype)
        }

        p.Phones = append(p.Phones, pn)
    }

    return p, nil
}

func main() {
    if len(os.Args) != 2 {
        log.Fatalf("Usage:  %s ADDRESS_BOOK_FILE\n", os.Args[0])
    }
    fname := os.Args[1]

    fmt.Println("osArgs-->>",os.Args[0],"-->>",os.Args[1])

    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)
        }
    }
    fmt.Println("osArgs after-->>")


    book := &go_protoc.AddressBook{}
    if err := proto.Unmarshal(in, book); err != nil {
        log.Fatalln("Failed to parse address book:", err)
    }

    addr, err := promptForAddress(os.Stdin)


    if err != nil {
        log.Fatalln("Error with address:", err)
    }
    book.People = append(book.People, addr)
    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)
    }
}

接下来通过命令行进行增加操作:

zhiliaodeMBP:src zhiliao$ ls
github.com      go_demo         go_protoc       other
zhiliaodeMBP:src zhiliao$ cd go_demo/
zhiliaodeMBP:go_demo zhiliao$ go run add_person.go txt
osArgs-->> /var/folders/6x/58xzg4n5681_429nfm1d7c0h0000gn/T/go-build149370200/b001/exe/add_person -->> txt
txt: File not found.  Creating new file.
osArgs after-->>
Enter person ID number: 111
Enter name: wyf
Enter email address (blank for none): 1@qq.com
Enter a phone number (or leave blank to finish): 123456
Is this a mobile, home, or work phone? mobile
Enter a phone number (or leave blank to finish): 
zhiliaodeMBP:go_demo zhiliao$ 

然后会序列化到txt文件中

我们来看下列出的功能list_people.go

package main

import (
    "fmt"
    "go_protoc"
    "io"
    "io/ioutil"
    "log"
    "os"

    "github.com/golang/protobuf/proto"
)

func writePerson(w io.Writer, p *go_protoc.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 go_protoc.Person_MOBILE:
            fmt.Fprint(w, "  Mobile phone #: ")
        case go_protoc.Person_HOME:
            fmt.Fprint(w, "  Home phone #: ")
        case go_protoc.Person_WORK:
            fmt.Fprint(w, "  Work phone #: ")
        }
        fmt.Fprintln(w, pn.Number)
    }
}

func listPeople(w io.Writer, book *go_protoc.AddressBook) {
    for _, p := range book.People {
        writePerson(w, p)
    }
}

func main() {
    if len(os.Args) != 2 {
        log.Fatalf("Usage:  %s ADDRESS_BOOK_FILE\n", os.Args[0])
    }
    fname := os.Args[1]

    in, err := ioutil.ReadFile(fname)
    if err != nil {
        log.Fatalln("Error reading file:", err)
    }
    book := &go_protoc.AddressBook{}
    if err := proto.Unmarshal(in, book); err != nil {
        log.Fatalln("Failed to parse address book:", err)
    }

    listPeople(os.Stdout, book)
}

依然通过控制台进行输出查看txt的信息

zhiliaodeMBP:go_demo zhiliao$ 
zhiliaodeMBP:go_demo zhiliao$ ls
add_person.go   list_people.go  txt
zhiliaodeMBP:go_demo zhiliao$ go run list_people.go txt
Person ID: 111
  Name: wyf
  E-mail address: 1@qq.com
  Mobile phone #: 123456
zhiliaodeMBP:go_demo zhiliao$ 

go语言中的另一个练习protobuf的例子

接下来我们通过一个例子来了解下在go中是如何使用protobuf的数据格式的
以下是这个demo的项目结构
这里写图片描述

首先看下hello.proto

syntax = "proto3";

//包名,通过protoc生成时go文件时
package go_protoc;

//手机类型
//枚举类型第一个字段必须为0
enum PhoneType {
    HOME = 0;
    WORK = 1;
}

//手机
message Phone {
    PhoneType type = 1;
    string number = 2;
}

//人
message Person {
    //后面的数字表示标识号
    int32 id = 1;
    string name = 2;
    //repeated表示可重复
    //可以有多个手机
    repeated Phone phones = 3;
}

//联系簿
message ContactBook {
    repeated Person persons = 1;
}

然后从hello.proto编译为hello.pb.go

zhiliaodeMBP:go_protoc zhiliao$ protoc ./hello.proto --go_out=./
zhiliaodeMBP:go_protoc zhiliao$ pwd
/Users/zhiliao/zhiliao/go/src/go_protoc
zhiliaodeMBP:go_protoc zhiliao$ ls
hello.pb.go     hello.proto
zhiliaodeMBP:go_protoc zhiliao$ 

然后看下go语言的代码safly.go

package main;

import (
    "github.com/golang/protobuf/proto"
    "go_protoc"
    "io/ioutil"
    "os"
    "fmt"
)

func write() {
    p1 := &go_protoc.Person{
        Id:   1,
        Name: "小张",
        Phones: []*go_protoc.Phone{
            {Type:go_protoc.PhoneType_HOME, Number:"111111111"},
            {Type:go_protoc.PhoneType_WORK, Number:"222222222"},
        },
    };


    p2 := &go_protoc.Person{
        Id:   2,
        Name: "小王",
        Phones: []*go_protoc.Phone{
            {Type:go_protoc.PhoneType_HOME, Number:"333333333"},
            {Type:go_protoc.PhoneType_WORK, Number:"444444444"},
        },
    };

    //创建地址簿
    book := &go_protoc.ContactBook{};
    book.Persons = append(book.Persons, p1);
    book.Persons = append(book.Persons, p2);

    //编码数据
    data, _ := proto.Marshal(book);
    //把数据写入文件
    ioutil.WriteFile("./txt", data, os.ModePerm);
}

func read() {
    //读取文件数据
    data, _ := ioutil.ReadFile("./txt");
    book := &go_protoc.ContactBook{};
    //解码数据
    proto.Unmarshal(data , book);
    for _, v := range book.Persons {
        fmt.Println(v.Id, v.Name);
        for _, vv := range v.Phones {
            fmt.Println(vv.Type, vv.Number);
        }
    }
}

func main() {
    write();
    read();
}

结果输出如下:

1 小张
HOME 111111111
WORK 222222222
2 小王
HOME 333333333
WORK 444444444

保存至文件内容如下:


&小张  111111111
 222222222
&小王  333333333
 444444444

最后看下编译生成的hello.pb.go的代码

// Code generated by protoc-gen-go. DO NOT EDIT.
// source: hello.proto

package go_protoc

/*
包名,通过protoc生成时go文件时
*/

import proto "github.com/golang/protobuf/proto"
import fmt "fmt"
import math "math"

// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
var _ = fmt.Errorf
var _ = math.Inf

// This is a compile-time assertion to ensure that this generated file
// is compatible with the proto package it is being compiled against.
// A compilation error at this line likely means your copy of the
// proto package needs to be updated.
const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package

// 手机类型
// 枚举类型第一个字段必须为0
type PhoneType int32

const (
    PhoneType_HOME PhoneType = 0
    PhoneType_WORK PhoneType = 1
)

var PhoneType_name = map[int32]string{
    0: "HOME",
    1: "WORK",
}

var PhoneType_value = map[string]int32{
    "HOME": 0,
    "WORK": 1,
}

func (x PhoneType) String() string {
    return proto.EnumName(PhoneType_name, int32(x))
}

func (PhoneType) EnumDescriptor() ([]byte, []int) {
    return fileDescriptor_61ef911816e0a8ce, []int{0}
}

// 手机
type Phone struct {
    Type                 PhoneType `protobuf:"varint,1,opt,name=type,proto3,enum=go_protoc.PhoneType" json:"type,omitempty"`
    Number               string    `protobuf:"bytes,2,opt,name=number,proto3" json:"number,omitempty"`
    XXX_NoUnkeyedLiteral struct{}  `json:"-"`
    XXX_unrecognized     []byte    `json:"-"`
    XXX_sizecache        int32     `json:"-"`
}

func (m *Phone) Reset()         { *m = Phone{} }
func (m *Phone) String() string { return proto.CompactTextString(m) }
func (*Phone) ProtoMessage()    {}
func (*Phone) Descriptor() ([]byte, []int) {
    return fileDescriptor_61ef911816e0a8ce, []int{0}
}
func (m *Phone) XXX_Unmarshal(b []byte) error {
    return xxx_messageInfo_Phone.Unmarshal(m, b)
}
func (m *Phone) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
    return xxx_messageInfo_Phone.Marshal(b, m, deterministic)
}
func (dst *Phone) XXX_Merge(src proto.Message) {
    xxx_messageInfo_Phone.Merge(dst, src)
}
func (m *Phone) XXX_Size() int {
    return xxx_messageInfo_Phone.Size(m)
}
func (m *Phone) XXX_DiscardUnknown() {
    xxx_messageInfo_Phone.DiscardUnknown(m)
}

var xxx_messageInfo_Phone proto.InternalMessageInfo

func (m *Phone) GetType() PhoneType {
    if m != nil {
        return m.Type
    }
    return PhoneType_HOME
}

func (m *Phone) GetNumber() string {
    if m != nil {
        return m.Number
    }
    return ""
}

// 人
type Person struct {
    // 后面的数字表示标识号
    Id   int32  `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"`
    Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"`
    // repeated表示可重复
    // 可以有多个手机
    Phones               []*Phone `protobuf:"bytes,3,rep,name=phones,proto3" json:"phones,omitempty"`
    XXX_NoUnkeyedLiteral struct{} `json:"-"`
    XXX_unrecognized     []byte   `json:"-"`
    XXX_sizecache        int32    `json:"-"`
}

func (m *Person) Reset()         { *m = Person{} }
func (m *Person) String() string { return proto.CompactTextString(m) }
func (*Person) ProtoMessage()    {}
func (*Person) Descriptor() ([]byte, []int) {
    return fileDescriptor_61ef911816e0a8ce, []int{1}
}
func (m *Person) XXX_Unmarshal(b []byte) error {
    return xxx_messageInfo_Person.Unmarshal(m, b)
}
func (m *Person) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
    return xxx_messageInfo_Person.Marshal(b, m, deterministic)
}
func (dst *Person) XXX_Merge(src proto.Message) {
    xxx_messageInfo_Person.Merge(dst, src)
}
func (m *Person) XXX_Size() int {
    return xxx_messageInfo_Person.Size(m)
}
func (m *Person) XXX_DiscardUnknown() {
    xxx_messageInfo_Person.DiscardUnknown(m)
}

var xxx_messageInfo_Person proto.InternalMessageInfo

func (m *Person) GetId() int32 {
    if m != nil {
        return m.Id
    }
    return 0
}

func (m *Person) GetName() string {
    if m != nil {
        return m.Name
    }
    return ""
}

func (m *Person) GetPhones() []*Phone {
    if m != nil {
        return m.Phones
    }
    return nil
}

// 联系簿
type ContactBook struct {
    Persons              []*Person `protobuf:"bytes,1,rep,name=persons,proto3" json:"persons,omitempty"`
    XXX_NoUnkeyedLiteral struct{}  `json:"-"`
    XXX_unrecognized     []byte    `json:"-"`
    XXX_sizecache        int32     `json:"-"`
}

func (m *ContactBook) Reset()         { *m = ContactBook{} }
func (m *ContactBook) String() string { return proto.CompactTextString(m) }
func (*ContactBook) ProtoMessage()    {}
func (*ContactBook) Descriptor() ([]byte, []int) {
    return fileDescriptor_61ef911816e0a8ce, []int{2}
}
func (m *ContactBook) XXX_Unmarshal(b []byte) error {
    return xxx_messageInfo_ContactBook.Unmarshal(m, b)
}
func (m *ContactBook) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
    return xxx_messageInfo_ContactBook.Marshal(b, m, deterministic)
}
func (dst *ContactBook) XXX_Merge(src proto.Message) {
    xxx_messageInfo_ContactBook.Merge(dst, src)
}
func (m *ContactBook) XXX_Size() int {
    return xxx_messageInfo_ContactBook.Size(m)
}
func (m *ContactBook) XXX_DiscardUnknown() {
    xxx_messageInfo_ContactBook.DiscardUnknown(m)
}

var xxx_messageInfo_ContactBook proto.InternalMessageInfo

func (m *ContactBook) GetPersons() []*Person {
    if m != nil {
        return m.Persons
    }
    return nil
}

func init() {
    proto.RegisterType((*Phone)(nil), "go_protoc.Phone")
    proto.RegisterType((*Person)(nil), "go_protoc.Person")
    proto.RegisterType((*ContactBook)(nil), "go_protoc.ContactBook")
    proto.RegisterEnum("go_protoc.PhoneType", PhoneType_name, PhoneType_value)
}

func init() { proto.RegisterFile("hello.proto", fileDescriptor_61ef911816e0a8ce) }

var fileDescriptor_61ef911816e0a8ce = []byte{
    // 221 bytes of a gzipped FileDescriptorProto
    0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x5c, 0x8e, 0x4f, 0x4b, 0xc3, 0x40,
    0x10, 0xc5, 0xdd, 0x34, 0x5d, 0xcd, 0x04, 0x4a, 0x1c, 0x44, 0xf6, 0x66, 0xc8, 0x69, 0x51, 0xc8,
    0xa1, 0xde, 0x3c, 0x2a, 0x82, 0x22, 0xd2, 0xb2, 0x88, 0x1e, 0xa5, 0x7f, 0x16, 0x1b, 0x4c, 0x76,
    0x96, 0x64, 0x3d, 0xe4, 0xdb, 0x4b, 0x86, 0x18, 0xa4, 0xb7, 0x37, 0xbc, 0xdf, 0x7b, 0x6f, 0x20,
    0x3d, 0xd8, 0xba, 0xa6, 0xd2, 0xb7, 0x14, 0x08, 0x93, 0x2f, 0xfa, 0x64, 0xb5, 0x2b, 0x9e, 0x61,
    0xbe, 0x3e, 0x90, 0xb3, 0xa8, 0x21, 0x0e, 0xbd, 0xb7, 0x4a, 0xe4, 0x42, 0x2f, 0x96, 0x17, 0xe5,
    0x84, 0x94, 0xec, 0xbf, 0xf5, 0xde, 0x1a, 0x26, 0xf0, 0x12, 0xa4, 0xfb, 0x69, 0xb6, 0xb6, 0x55,
    0x51, 0x2e, 0x74, 0x62, 0xc6, 0xab, 0x78, 0x07, 0xb9, 0xb6, 0x6d, 0x47, 0x0e, 0x17, 0x10, 0x55,
    0x7b, 0x6e, 0x9a, 0x9b, 0xa8, 0xda, 0x23, 0x42, 0xec, 0x36, 0x8d, 0x1d, 0x79, 0xd6, 0xa8, 0x41,
    0xfa, 0xa1, 0xb8, 0x53, 0xb3, 0x7c, 0xa6, 0xd3, 0x65, 0x76, 0xbc, 0x68, 0x46, 0xbf, 0xb8, 0x83,
    0xf4, 0x81, 0x5c, 0xd8, 0xec, 0xc2, 0x3d, 0xd1, 0x37, 0xde, 0xc0, 0xa9, 0xe7, 0x99, 0x4e, 0x09,
    0x4e, 0x9e, 0xff, 0x4f, 0xb2, 0x63, 0xfe, 0x88, 0xeb, 0x2b, 0x48, 0xa6, 0xf7, 0xf1, 0x0c, 0xe2,
    0xa7, 0xd5, 0xeb, 0x63, 0x76, 0x32, 0xa8, 0x8f, 0x95, 0x79, 0xc9, 0xc4, 0x56, 0x72, 0xf0, 0xf6,
    0x37, 0x00, 0x00, 0xff, 0xff, 0xe6, 0x4b, 0xbd, 0x93, 0x20, 0x01, 0x00, 0x00,
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值