[TOC] # 优缺点 ## protoBuf的优点 Protobuf 有如 XML,不过它更小、更快、也更简单。你可以定义自己的数据结构,然后使用代码生成器生成的代 码来读写这个数据结构。你甚至可以在无需重新部署程序的情况下更新数据结构。只需使用 Protobuf 对数据结构 进行一次描述,即可利用各种不同语言或从各种不同数据流中对你的结构化数据轻松读写。 它有一个非常棒的特性,即“向后”兼容性好,人们不必破坏已部署的、依靠“老”数据格式的程序就可以对数据结构 进行升级。 Protobuf 语义更清晰,无需类似 XML 解析器的东西(因为 Protobuf 编译器会将 .proto 文件编译生成对应的数据 访问类以对 Protobuf 数据进行序列化、反序列化操作)。使用 Protobuf 无需学习复杂的文档对象模型, Protobuf 的编程模式比较友好,简单易学,同时它拥有良好的文档和示例,对于喜欢简单事物的人们而言, Protobuf 比其他的技术更加有吸引力。 ## ProtoBuf 的不足 Protobuf 与 XML 相比也有不足之处。它功能简单,无法用来表示复杂的概念。 XML 已经成为多种行业标准的编写工具,Protobuf 只是 Google 公司内部使用的工具,在通用性上还差很多。 由 于文本并不适合用来描述数据结构,所以 Protobuf 也不适合用来对基于文本的标记文档(如 HTML)建模。另 外,由于 XML 具有某种程度上的自解释性,它可以被人直接读取编辑,在这一点上 Protobuf 不行,它以二进制的 方式存储,除非你有 .proto 定义,否则你没法直接读出 Protobuf 的任何内容 # 安装 ~~~ #下载 protoBuf: $ git clone https://github.com/protocolbuffers/protobuf.git #或者直接将压缩包拖入后解压 unzip protobuf.zip #安装依赖库 $ sudo apt-get install autoconf automake libtool curl make g++ unzip libffi- dev -y #安装 $ cd protobuf/ $ ./autogen.sh $ ./configure $ make $ sudo make install $ sudo ldconfig # 刷新共享库 很重要的一步啊 #安装的时候会比较卡 #成功后需要使用命令测试 $ protoc –h ~~~ **获取proto包** ~~~ #Go语言的proto API接口 go get -u github.com/golang/protobuf/proto ~~~ **安装protoc-gen-go插件** 它是一个 go程序,编译它之后将可执行文件复制到\\bin目录 ~~~ #安装 $ go get -v -u github.com/golang/protobuf/protoc-gen-go #编译 $ cd $GOPATH/src/github.com/golang/protobuf/protoc-gen-go/ $ go build #将生成的 protoc-gen-go可执行文件,放在/bin目录下 $ sudo cp protoc-gen-go /bin/ ~~~ # protobuf的语法 要想使用 protobuf必须得先定义 proto文件。 所以得先熟悉 protobuf的消息定义的相关语法。 定义一个消息类型 ~~~ syntax = "proto3"; message PandaRequest { string name = 1; int32 shengao = 2; repeated int32 tizhong = 3; } ~~~ PandaRequest消息格式有3个字段,在消息中承载的数据分别对应于每一个字段。其中每个字段都有一个名字和 一种类型。 文件的第一行指定了你正在使用proto3语法:如果你没有指定这个,编译器会使用proto2。这个指定语法行必须 是文件的非空非注释的第一个行。 在上面的例子中,所有字段都是标量类型:两个整型(shengao和tizhong),一个string类型(name)。 **Repeated** 关键字表示重复的那么在go语言中用切片进行代表 正如上述文件格式,在消息定义中,每个字段都有唯一的一个标识符 **添加更多消息类型** 在一个.proto文件中可以定义多个消息类型。在定义多个相关的消息的时候,这一点特别有用——例如,如果想定义与SearchResponse消息类型对应的回复消息格式的话,你可以将它添加到相同的.proto文件中 ~~~ syntax = "proto3"; message PandaRequest { string name = 1; int32 shengao = 2; int32 tizhong = 3; } message PandaResponse { ... } ~~~ # 从.proto文件生成了什么? 当用protocol buffer编译器来运行.proto文件时,编译器将生成所选择语言的代码,这些代码可以操作在.proto文件中定义的消息类型,包括获取、设置字段值,将消息序列化到一个输出流中,以及从一个输入流中解析消息。 对C++来说,编译器会为每个.proto文件生成一个.h文件和一个.cc文件,.proto文件中的每一个消息有一个对应的 类。 对Python来说,有点不太一样——Python编译器为.proto文件中的每个消息类型生成一个含有静态描述符的模 块,,该模块与一个元类(metaclass)在运行时(runtime)被用来创建所需的Python数据访问类。 对go来说,编译器会为每个消息类型生成了一个.pd.go文件 # 标准数据类型 一个标量消息字段可以含有一个如下的类型——该表格展示了定义于.proto文件中的类型,以及与之对应的、在自 动生成的访问类中定义的类型: ![](https://box.kancloud.cn/7cac70ad0e1368166223dd8e22abe3c0_655x654.png) # 默认值 ~~~ 当一个消息被解析的时候,如果被编码的信息不包含一个特定的元素,被解析的对象锁对应的域被设置位一个默认 值,对于不同类型指定如下: ~~~ 对于strings,默认是一个空string 对于bytes,默认是一个空的bytes 对于bools,默认是false 对于数值类型,默认是0 # 使用其他消息类型 你可以将其他消息类型用作字段类型。 例如,假设在每一个PersonInfo消息中包含Person消息,此时可以在相同 的.proto文件中定义一个Result消息类型,然后在PersonInfo消息中指定一个Person类型的字段 ~~~ message PersonInfo { repeated Person info = 1; } message Person { string name = 1; int32 shengao = 2; repeated int32 tizhong = 3; } ~~~ # 使用proto2消息类型 在你的proto3消息中导入proto2的消息类型也是可以的,反之亦然,然后proto2枚举不可以直接在proto3的标识 符中使用(如果仅仅在proto2消息中使用是可以的)。 嵌套类型 你可以在其他消息类型中定义、使用消息类型,在下面的例子中,Person消息就定义在PersonInfo消息内,如: ~~~ message PersonInfo { message Person { string name = 1; int32 shengao = 2; repeated int32 tizhong = 3; } repeated Person info = 1; } ~~~ 如果你想在它的父消息类型的外部重用这个消息类型,你需要以PersonInfo.Person的形式使用它,如: ~~~ message PersonMessage { PersonInfo.Person info = 1; } ~~~ 当然,你也可以将消息嵌套任意多层,如: ~~~ message Grandpa { message Father { // Level 1 message son { // Level 2 string name = 1; int32 age = 2; } } message Uncle { // Level 1 message Son { // Level 2 string name = 1; int32 age = 2; } } } ~~~ # 定义服务(Service) 如果想要将消息类型用在RPC(远程方法调用)系统中,可以在.proto文件中定义一个RPC服务接口,protocol buffer 编译器将会根据所选择的不同语言生成服务接口代码及存根。如,想要定义一个RPC服务并具有一个方法,该方法 能够接收 SearchRequest并返回一个SearchResponse,此时可以在.proto文件中进行如下定义: ~~~ service SearchService { //rpc 服务的函数名 (传入参数)返回(返回参数) rpc Search (SearchRequest) returns (SearchResponse); } ~~~ 最直观的使用protocol buffer的RPC系统是gRPC一个由谷歌开发的语言和平台中的开源的RPC系统,gRPC在使用 protocl buffer时非常有效,如果使用特殊的protocol buffer插件可以直接为您从.proto文件中产生相关的RPC代 码。 如果你不想使用gRPC,也可以使用protocol buffer用于自己的RPC实现,你可以从proto2语言指南中找到更多信息 # 生成访问类 可以通过定义好的.proto文件来生成Java,Python,C++, Ruby, JavaNano, Objective-C,或者C# 代码,需要基 于.proto文件运行protocol buffer编译器protoc。如果你没有安装编译器,下载安装包并遵照README安装。对于 Go,你还需要安装一个特殊的代码生成器插件。你可以通过GitHub上的protobuf库找到安装过程 通过如下方式调用protocol编译器: ~~~ protoc --proto_path=IMPORT_PATH --cpp_out=DST_DIR --python_out=DST_DIR --go_out=DST_DIR path/to/file.proto ~~~ ~~~ protoc --go_out=./ *.proto ~~~ IMPORT\_PATH声明了一个.proto文件所在的解析import具体目录。如果忽略该值,则使用当前目录。如果有多个 目录则可以多次调用\--proto\_path,它们将会顺序的被访问并执行导入。\-I=IMPORT\_PATH是\--proto\_path的简化 形式。 当然也可以提供一个或多个输出路径: `--cpp_out` 在目标目录`DST_DIR`中产生C++代码,可以在C++代码生成参考中查看更多。 `--python_out` 在目标目录 `DST_DIR` 中产生Python代码,可以在Python代码生成参考中查看更多。 `--go_out` 在目标目录 `DST_DIR` 中产生Go代码,可以在GO代码生成参考中查看更多。 作为一个方便的拓展,如 果DST_DIR以.zip或者.jar结尾,编译器会将输出写到一个ZIP格式文件或者符合JAR标准的.jar文件中。注意如果输 出已经存在则会被覆盖,编译器还没有智能到可以追加文件 \- 你必须提议一个或多个.proto文件作为输入,多个.proto文件可以只指定一次。虽然文件路径是相对于当前目录 的,每个文件必须位于其IMPORT\_PATH下,以便每个文件可以确定其规范的名称。 # 测试下 在这个prototext包下创建text.proto文件 ~~~ syntax = "proto3"; package prototext; message Test { //姓名 string name = 1; //体重 repeated int32 tizhing = 2; //身高 repeated int64 shengao = 3; //格言 string motto = 4; } ~~~ 生成下 ~~~ protoc --go_out=./ *.proto ~~~ 写个测试文件,对protoBuf进行编码解码 ~~~ package main import ( "fmt" "github.com/gogo/protobuf/proto" "studygo/prototext" ) func main() { text := &prototext.Test{ //里面写我们的东西 Name: "panda", Tizhing: []int32{120, 125, 198, 150}, Shengao: []int64{1, 44, 555}, Motto: "我的 hello world", } fmt.Println(text) //proto编码 ([]byte, error) bytes, err := proto.Marshal(text) if err != nil { fmt.Println("编码失败") } //编码后打印 fmt.Println(bytes) //解码,解码后会放到这里 newText := &prototext.Test{} //proto的解码 e := proto.Unmarshal(bytes, newText) if e != nil { fmt.Println("解码失败") } //打印解码的结果 newText.String() fmt.Println(newText) fmt.Println(newText.Motto) } ~~~ # GRPC使用protobuf gRPC默认使用protoBuf,这是 Google开源的一套成熟的结构数据序列化机制(当然也可以使用其他数据格式如 JSON)。正如你将在下方例子里所看到的,你用 proto files创建 gRPC服务,用 protoBuf消息类型来定义方法参 数和返回类型 ![](https://box.kancloud.cn/b6190570620acc08bbe100ccee6f7fde_726x345.png) ## protobuf协议定义 创建一个 protobuf package, 如: my\_rpc\_proto; 在$GOPATH/src/下创建 /my\_grpc\_proto/文件夹 里面创建 protobuf协议文件 helloServer.proto