[TOC] [protocol-buffers](https://developers.google.com/protocol-buffers) ## 简述 ### protobuf 不觉得粘包问题 ## 安装 ### 方案一 protobuf `go get github.com/golang/protobuf/protoc-gen-go ` 生成go文件 `protoc --go_out=. *.proto` ### gogo库 gogo库基于官方库开发,增加了很多的功能,包括: * 快速的序列化和反序列化 * 更规范的Go数据结构 * goprotobuf兼容 * 可选择的产生一些辅助方法,减少使用中的代码输入 * 可以选择产生测试代码和benchmark代码 * 其它序列化格式 * gogo同样支持grpc: protoc --gofast_out=plugins=grpc:. my.proto #### gofast 速度优先 不支持其它gogoprotobuf extensions。 ``` go get github.com/gogo/protobuf/protoc-gen-gofast protoc --gofast_out=. myproto.proto ``` #### gogofast、gogofaster、gogoslick 更快的速度、更多的产生代码 - gogofast类似gofast,但是会导入gogoprotobuf. - gogofaster类似gogofast, 不会产生XXX_unrecognized指针字段,可以减少垃圾回收时间。 - gogoslick类似gogofaster,但是可以增加一些额外的方法gostring和equal等等 ``` go get github.com/gogo/protobuf/proto //binary = protoc-gen-gogofast、protoc-gen-gogofaster 、protoc-gen-gogoslick go get github.com/gogo/protobuf/{binary} go get github.com/gogo/protobuf/gogoproto protoc -I=. -I=$GOPATH/src -I=$GOPATH/src/github.com/gogo/protobuf/protobuf --{binary}_out=. myproto.proto ``` #### protoc-gen-gogo ``` go get github.com/gogo/protobuf/proto go get github.com/gogo/protobuf/jsonpb go get github.com/gogo/protobuf/protoc-gen-gogo go get github.com/gogo/protobuf/gogoproto ``` ## 教程 ### proto3的改变 1. 移除了原始值字段的出现逻辑。 1. 移除了required字段 1. 移除了缺省值 1. 移除了unknown字段 (3.5中又加上了) 1. 移除了扩展,使用Any代替 1. 修复了未知的枚举值的语义 1. 添加了map类型 1. 添加了一些标准类似,比如time、动态数据的呈现 1. 可以使用JSON编码代替二进制proto编码 ## `.proto`转编程语言 ``` //转go protoc --go_out=. test.proto //转c++ protoc --cpp_out=. test.proto //转java protoc --java_out=. test.proto //转python protoc --python_out=. test.proto //转python protoc --java_out=. test.proto //转php protoc --php_out=. test.proto ``` ## 编译命令 ``` protoc -I=. -I/usr/local/include -I=$(GOPATH)/src --go_out=. simple.proto -I 指定protoc的搜索import的proto的文件夹。在MacOS操作系统中protobuf把一些扩展的proto放在了/usr/local/include对应的文件夹中,一些第三方的Go库放在了gopath对应的包下,所以这里都把它们加上了。对于这个简单的例子,实际是不需要的 ``` ## 关键字 ## 格式的定义 `[ "repeated" ] type fieldName "=" fieldNumber [ "[" fieldOptions "]" ] ";"` 或 `type fieldName "=" fieldNumber` ### 版本定义 `syntax = "proto3"; ` or`syntax = "proto2"; ` ### package 定义包名 ``` package foo.bar; ``` ### repeated 允许重复 .proto ``` message Person { string name = 1; int32 id = 2; repeated string email = 3; } ``` demo.go ``` p := &Person{ Name: "smallnest", Id: 9527, Email: []string{"test@example.com"}, } ``` ### option option可以用在proto的scope中,或者message、enum、service的定义中。 可以是Protobuf定义的option,或者自定义的option。 option的定义格式是`"option" optionName "=" constant ";"` 比如: `option java_package = "com.example.foo"; ` 一些Protobuf定义的option: ``` java_package java_multiple_files java_outer_classname optimize_for cc_enable_arenas objc_class_prefix deprecated ``` ### **普通字段** > [官网类型对应其他语言](https://developers.google.com/protocol-buffers/docs/proto3#scalar) * 数字类型: double、float、int32、int64、uint32、uint64、sint32、sint64: 存储长度可变的浮点数、整数、无符号整数和有符号整数 * 存储固定大小的数字类型:fixed32、fixed64、sfixed32、sfixed64: 存储空间固定 * 布尔类型: bool * 字符串: string * bytes: 字节数组 * messageType: 消息类型 * enumType:枚举类型 ![UTOOLS1577338933390.png](http://yanxuan.nosdn.127.net/f1ec32b0cdda86ac5476f56552e0925e.png) ![UTOOLS1577338958932.png](http://yanxuan.nosdn.127.net/3c3a41d3081d73b48d77829a44a16720.png) ### Oneof 如果你有一组字段,同时最多允许这一组中的一个字段出现,就可以使用`Oneof`定义这一组字段,这有点Union的意思,但是Oneof允许你设置零各值。 因为proto3没有办法区分正常的值是否是设置了还是取得缺省值(比如int64类型字段,如果它的值是0,你无法判断数据是否包含这个字段,因为0几可能是数据中设置的值,也可能是这个字段的零值),所以你可以通过Oneof取得这个功能,因为Oneof有判断字段是否设置的功能。 ``` syntax = "proto3"; package abc; message OneofMessage { oneof test_oneof { string name = 4; int64 value = 9; } } ``` >`oneof`字段不能同时使用`repeated`。 ### map类型 `map<int64,string> values = 1; ` ### Reserved 也就是忽略某些字段,可以通过字段编号范围或者字段名称指定保留的字段 ``` syntax = "proto3"; package abc; message AllNormalypes { reserved 2, 4 to 6; reserved "field14", "field11"; double field1 = 1; // float field2 = 2; int32 field3 = 3; // int64 field4 = 4; // uint32 field5 = 5; // uint64 field6 = 6; sint32 field7 = 7; sint64 field8 = 8; fixed32 field9 = 9; fixed64 field10 = 10; // sfixed32 field11 = 11; sfixed64 field12 = 12; bool field13 = 13; // string field14 = 14; bytes field15 = 15; } ``` > 声明保留的字段你就不要再定义了,需注释,否则编译的时候会出错。 ### 枚举类型 避免在同一个package定义重名的枚举字段 ``` enum EnumAllowingAlias { option allow_alias = true; UNKNOWN = 0; STARTED = 1; RUNNING = 1; } enum EnumNotAllowingAlias { UNKNOWN2 = 0; STARTED2 = 1; // RUNNING = 1; } ``` > 设置 `allow_alias` 允许重复字段编号 如`STARTED`和`RUNNING ` > **第一个枚举值必须是0**,而且必须定义 枚举类型定义到message中 ``` message SearchRequest { string query = 1; int32 page_number = 2; int32 result_per_page = 3; enum Corpus { UNIVERSAL = 0; WEB = 1; IMAGES = 2; LOCAL = 3; NEWS = 4; PRODUCTS = 5; VIDEO = 6; } Corpus corpus = 4; } ``` ### 使用其它类型 在SearchResponse 中调用 Result 类型 ``` message SearchResponse { repeated Result results = 1; } message Result { string url = 1; string title = 2; repeated string snippets = 3; } ``` ### 嵌套类型 ``` message SearchResponse { message Result { string url = 1; string title = 2; repeated string snippets = 3; } repeated Result results = 1; } ``` ### Any Any字段允许你处理嵌套数据,并不需要它的proto定义。一个Any以bytes呈现序列化的消息,并且包含一个URL作为这个类型的唯一标识和元数据 ``` import "google/protobuf/any.proto"; message ErrorStatus { string message = 1; repeated google.protobuf.Any details = 2; } ```