热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

golang基础protobuf使用

golang基础-protobuf使用,Go语言社区,Golang程序员人脉社


      • Mac安装

      • 摘自谷歌protobuf官网案例

      • go语言中的另一个练习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, "%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的例子

接下来我们通过一个例子来了解下在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,
}


推荐阅读
  • 本文探讨了Web开发与游戏开发之间的主要区别,旨在帮助开发者更好地理解两种开发领域的特性和需求。文章基于作者的实际经验和网络资料整理而成。 ... [详细]
  • 初探Java编程:从入门到实践
    本文旨在为初学者提供Java编程的基础知识,涵盖程序、算法、流程图的概念,以及JDK环境的配置和Eclipse的使用方法。 ... [详细]
  • 本文探讨如何利用Java反射技术来模拟Webwork框架中的URL解析过程。通过这一实践,读者可以更好地理解Webwork及其后续版本Struts2的工作原理,尤其是它们在MVC架构下的角色。 ... [详细]
  • Java 架构:深入理解 JDK 动态代理机制
    代理模式是 Java 中常用的设计模式之一,其核心在于代理类与委托类共享相同的接口。代理类主要用于为委托类提供预处理、过滤、转发及后处理等功能,以增强或改变原有功能的行为。 ... [详细]
  • 大数据基础:JavaSE_day06 ... [详细]
  • 本文深入探讨Java编程语言的关键特性,包括但不限于其简洁性、强大的面向对象能力、跨平台兼容性、安全机制、高效性能及多线程支持等方面。文章旨在为开发者提供全面理解Java特性的指导。 ... [详细]
  • MapReduce原理是怎么剖析的
    这期内容当中小编将会给大家带来有关MapReduce原理是怎么剖析的,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。1 ... [详细]
  • 本文介绍了在CentOS 6.4系统中安装MySQL 5.5.37时遇到的启动失败和PID文件问题,并提供了详细的解决方案,包括日志分析、权限检查等步骤。 ... [详细]
  • Java EE CDI:解决依赖关系冲突的实例
    在本教程中,我们将探讨如何在Java EE的CDI(上下文和依赖注入)框架中有效解决依赖关系的冲突问题。通过学习如何使用限定符,您将能够为应用程序的不同客户端提供多种接口实现,并确保每个客户端都能正确调用其所需的实现。 ... [详细]
  • 本文提供了详细的步骤,指导如何在Ubuntu系统中配置ASOP源码的编译环境,特别强调了使用国内镜像加速下载过程的方法。若遇到文章加载问题或图片失效,建议访问原文链接获取最新信息。 ... [详细]
  • 前言无论是对于刚入行工作还是已经工作几年的java开发者来说,面试求职始终是你需要直面的一件事情。首先梳理自己的知识体系,针对性准备,会有事半功倍的效果。我们往往会把重点放在技术上 ... [详细]
  • 在Java开发中,使用BASE64编码通常可以直接利用JDK内置的库。然而,在Android平台上,由于安全性和兼容性的考虑,直接引用JDK中的`sun.misc.BASE64Decoder`会导致错误,因此需要引入第三方库来实现相同的功能。 ... [详细]
  • Python基础教程:struct模块与格式化字符详解
    本文详细介绍了Python中struct模块的功能,以及如何利用格式化字符实现Python与C语言结构体之间的数据转换。文章通过具体实例讲解了struct模块的主要方法及其应用场景。 ... [详细]
  • ServletContext接口在Java Web开发中扮演着重要角色,它提供了一种方式来获取关于整个Web应用程序的信息。通过ServletContext,开发者可以访问初始化参数、共享数据以及应用资源。 ... [详细]
  • Flowable 6.6.0 表单引擎在Web应用中的集成与使用
    本文档提供了Flowable 6.6.0版本中表单引擎在Web应用程序中的配置和使用指南,包括表单引擎的初始化、配置以及在Web环境下的具体实现方法。 ... [详细]
author-avatar
我是王健值得信赖
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有