前些日子了解过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
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=./
参考链接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, "%dn", &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_FILEn", 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_FILEn", 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的数据格式的
以下是这个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,
}