【轻知识】(糙译)Go使用proto3、官方示例代码

个人觉得,有谷歌翻译,百度翻译。加上自己的理解。自个看看官方文档也还不错。
下面98%是谷歌翻译跟百度翻译的。剩余2%是我自己按照自己的理解。
免责声明:粗糙翻译,随便看看哈。想纠正的。留言即可。本人水平有限。

Protocol Buffer Basics: Go

通过创建一个简单的示例应用程序,它向您展示如何:

  • 在.proto文件中定义消息格式。
  • 使用协议缓冲区编译器。
  • 使用Go协议缓冲区API来编写和读取消息。

"生成的代码指南"
编码参考
GO api参考

为何使用protobuf

假设我们的示例是一个非常简单的“地址簿”应用程序,可以在文件中读取和写入人员的联系人详细信息。地址簿中的每个人都有姓名,ID,电子邮件地址和联系电话号码。

那么序列化和恢复这样结构(序列化与反序列化)的几种方法:

  • 使用gobs序列化Go数据结构。这是Go特定环境中的一个很好的解决方案,但如果您需要与为其他平台编写的应用程序共享数据,它将无法正常工作。

  • 您可以发明一种特殊的方法将数据项编码为单个字符串 - 例如将4个整数编码为“12:3:-23:67”。这是一种简单而灵活的方法,虽然它确实需要编写一次性编码和解析代码,并且解析会产生较小的运行时成本。这最适合编码非常简单的数据。

  • 将数据序列化为XML。这种方法非常有吸引力,因为XML(有点)是人类可读的,并且有许多语言的绑定库。如果您想与其他应用程序/项目共享数据,这可能是一个不错的选择。然而,XML是众所周知的空间密集型,并且编码/解码它会对应用程序造成巨大的性能损失。此外,导航XML DOM树比通常在类中导航简单字段要复杂得多。

协议缓冲区是一种灵活、高效、自动化的解决方案,可以精确地解决这个问题。使用协议缓冲区,可以编写要存储的数据结构的.proto描述。由此,协议缓冲区编译器创建了一个类,该类用有效的二进制格式实现协议缓冲区数据的自动编码和解析。生成的类为组成协议缓冲区的字段提供getter和setter,并负责将协议缓冲区作为一个单元读写的详细信息。重要的是,协议缓冲区格式支持随着时间的推移扩展格式的思想,这样代码仍然可以读取用旧格式编码的数据。

示例代码https://github.com/protocolbuffers/protobuf/tree/master/examples

定义你的protocol 格式

要创建通讯簿应用程序,您需要从.proto文件开始。.proto文件中的定义很简单:为要序列化的每个数据结构添加消息,然后为消息中的每个字段指定名称和类型。在我们的示例中,定义消息的.proto文件是addressbook.proto。
.proto文件以包声明开头,这有助于防止不同项目之间的命名冲突。

syntax = "proto3";
package tutorial;

import "google/protobuf/timestamp.proto"

在go中,包名就是作为Go的包名。除非你指定了一个go_package(编译时可以指定go_package)。你要是提供了go_package ,你定义的包名要避免与非Go语言的Protobuf 的命名空间的冲突。

vscode 可以按照proto3的插件,这样编辑起来比较方便。

下面,你有了你的消息定义。一个消息是包含一组类型字段的聚合。许多标准的简单数据类型都可以作为字段类型,包括bool,int32, float,double,和string。您还可以使用其他消息类型作为字段类型,为邮件添加更多结构。

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;
    google.protobuf.Timestamp last_updated = 5;
}

message AddressBook {
    repeated Person people = 1;
}

在上面的示例中,Person消息包含 PhoneNumber消息,而AddressBook消息包含Person消息。您甚至可以定义嵌套在其他消息中的消息类型 -​​ 如您所见, PhoneNumber类型在内部定义Person。您还可以定义enum,如果你希望你的领域之一,有预定义的值列表中的一个类型-在这里你要指定一个电话号码可以是一个MOBILE,HOME或 WORK

每个元素上的“= 1”,“= 2”标记标识该字段在二进制编码中使用的唯一“标记”。

如果未设置字段值, 则使用默认值:数字类型为零,字符串为空字符串,bools为false。对于嵌入式消息,默认值始终是消息的“默认实例”或“原型”,其中没有设置其字段。调用访问器以获取尚未显式设置的字段的值始终返回该字段的默认值。

如果是字段repeated,则字段可以重复任意次数(包括零)。重复值的顺序将保留在协议缓冲区中。将重复字段视为动态大小的数组。

编译protobuf

下载protoc(windows。下载,把protoc的bin目录放到环境变量里。mac可以使用brew install 去安装)。

生成go所需要的pb.go文件需要 go get -u github.com/golang/protobuf/protoc-gen-go。

现在运行编译器,指定源目录(应用程序的源代码所在的位置 - 如果不提​​供值,则使用当前目录),目标目录(您希望生成的代码在哪里;通常同$SRC_DIR) ,以指定你要编译的.proto。

protoc -I=$SRC_DIR --go_out=$DST_DIR $SRC_DIR/addressbook.proto

输出go用的文件用go_out,其他语言有其他语言的选项。

protobuf API

我在我工程的目录下抄写了官网代码,并在当前目录下执行protoc addressbook.proto --go_out=./

生成的addressbook.pb.go为你提供了下面可用的类型:

  • AddreBook包含了People

  • Person里面有Name,Id,Email,Phones.

  • Person_PhoneNumber包含Number 和Type

  • 类型 Person_PhoneType和Person.PhoneType枚举

去看下官方示例。

如何创建Person实例

p := pb.Person{
        Id:    1234,
        Name:  "John Doe",
        Email: "jdoe@example.com",
        Phones: []*pb.Person_PhoneNumber{
                {Number: "555-4321", Type: pb.Person_HOME},
        },
}

写一个消息

使用协议缓冲区的全部目的是序列化您的数据,以便可以在其他地方解析它。在Go中,您使用proto 库的Marshal 函数来序列化协议缓冲区数据。指向协议缓冲区消息的指针struct实现proto.Message 接口。调用proto.Marshal返回以其有线格式编码的protobuf。例如,我们在add_person命令中使用此函数 :

book := &pb.AddressBook{}
// ...

// 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)
}

读消息

要解析编码消息,请使用proto库的 Unmarshal 函数。调用此方法将数据解析buf为协议缓冲区并将结果放入pb。因此,要在list_people命令中解析文件 ,我们使用:

// 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)
}

扩展protobuf

当你代码上线之后,避免不了修改proto文件的定义。你需要注意一些兼容性规则。在你的新版本代码中:

  • 不能更改现有字段的编号。
  • 可以删除字段
  • 可以添加字段,新增字段使用新的编号(从没用过的,哪怕是给删除了字段使用过的)

遵循了这些规则,旧代码在读消息的时候会忽略你的新代码。对于删除的字段,旧代码也会给其默认值,删除的repeated字段将为空。新代码也

旧消息中不会出现新字段,因此需要使用默认值执行合理操作。使用特定于类型的 默认值 :对于字符串,默认值为空字符串。对于布尔值,默认值为false。对于数字类型,默认值为零。

示例代码跑一跑

官方的代码示例。https://github.com/protocolbuffers/protobuf

首先,我go get下来了。然后用vscode 打开了这个项目。

在proto文件目录下我创建了一个文件夹tutorial(当然文档里面有个Makefile会帮你做这件事情,但我windows不好使,手动来)。以便于生成pb.go文件放入其中(不然 list_people_test.go 里面引入的pb找不到)。

第一例子是以下是list_people命令的单元测试示例 ,说明如何创建Person实例:

go test -v list_people_test.go list_people.go(-v是看命令都干了些啥)

写一条消息(Writing a Message)

go run add_person.go addressbook.data

然后就输入数据写入到指定的文件中。

Enter person ID number: 123434
Enter name: mike
Enter email address (blank for none): mike@gmail.com
Enter a phone number (or leave blank to finish): 178933
Is this a mobile, home, or work phone? home
Enter a phone number (or leave blank to finish): 98783
Is this a mobile, home, or work phone? work
Enter a phone number (or leave blank to finish): 123333
Is this a mobile, home, or work phone? mobile

打开addressbook.data,看看内容

"
�mike�{��mike@gmail.com"
�178933
;
�mike������mike@gmail.com"

�178933��"  
�98783��"
�123333

读一条消息(Reading a Message)

go run list_people.go addressbook.data

Person ID: 123
  Name: mike
  E-mail address: mike@gmail.com
  Mobile phone #: 178933
Person ID: 123434
  Name: mike
  E-mail address: mike@gmail.com
  Home phone #: 178933
  Work phone #: 98783

我的思考

剥去了丝丝神秘?就像我当年刚入行让我给移动端写接口,我一头雾水什么是接口?前辈说ajax写过吧。写过……_

json序列化反序列化知道吧。json改成proto。传的参数(类型是Message)是proto文件生成的pb.go文件中的Message。也可以看看Message类型是什么?

type Message interface {
    Reset()
    String() string
    ProtoMessage()
}

只要实现了这三个接口的,就是Message类型了(想想Duck Type)。

我在我工程下,简单的练习了下。

p := pb.Person{
    Id:    1234,
    Name:  "Mike",
    Email: "mike@gmail.com",
    Phones: []*pb.Person_PhoneNumber{
        {Number: "555-4321", Type: pb.Person_HOME},
    },
}
book := &pb.AddressBook{
    People: []*pb.Person{&p},
}
out, err := proto.Marshal(book)
if err != nil {
    log.Fatalln("Failed to encode address book:", err)
}
fmt.Println(out)
book1 := &pb.AddressBook{}
proto.Unmarshal(out, book1)
fmt.Printf("%s\n", string(out))
fmt.Printf("%v", book1)

输出

[10 39 10 4 77 105 107 101 16 210 9 26 14 109 105 107 101 64 103 109 97 105 108 46 99 111 109 34 12 10 8 53 53 53 45 52 51 50 49 16 1]

'
�Mike�� ��mike@gmail.com"
555-4321��
people:<name:"Mike" id:1234 email:"mike@gmail.com" phones:<number:"555-4321" type:HOME > >

参考资料:

《go test 测试用例那些事》https://www.cnblogs.com/li-peng/p/10036468.html

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,569评论 4 363
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,499评论 1 294
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,271评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,087评论 0 209
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,474评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,670评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,911评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,636评论 0 202
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,397评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,607评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,093评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,418评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,074评论 3 237
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,092评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,865评论 0 196
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,726评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,627评论 2 270

推荐阅读更多精彩内容