本章主要介绍了如何在 proto 文件中引入其他 proto 文件。

1. 概述

Protocol buffers 是一种语言无关、平台无关的可扩展机制或者说是数据交换格式,用于序列化结构化数据。与 XML、JSON 相比,Protocol buffers 序列化后的码流更小、速度更快、操作更简单。

Protocol buffers are a language-neutral, platform-neutral extensible mechanism for serializing structured data.

2. 详解

基本定义

一个简单的 protobuf 文件定义如下:

1
2
3
4
5
6
7
syntax = "proto3";
option go_package = "protobuf/import;proto";
package import;

message Computer {
  string name = 1;
}

syntax = “proto3”;—指定使用 proto3 语法

option go_package = “protobuf/import;proto”;—前一个参数用于指定生成文件的位置,后一个参数指定生成的 .go 文件的 package 。具体语法如下:

1
option go_package = "{out_path};out_go_package";

注意:这里指定的 out_path 并不是绝对路径,只是相对路径或者说只是路径的一部分,和 protoc 的 --go_out 拼接后才是完整的路径。

也使用--go_opt=paths=source_relative直接指定 protoc 中 指定的是绝对路径,这样就不会去管 protobuf 文件中指定的路径。

package import;—表示当前 protobuf 文件属于 import包,这个package不是 Go 语言中的那个package。

这个 package 主要在导入外部 proto 文件时用到。

导入其他proto文件

要导入其他 proto 文件只需要使用import键字,具体如下:

1
import "protobuf/import/component.proto";

导入后则通过被导入文件包名.结构体名使用。

component.proto 文件中 package 指定为 import,所以这里通过 import.CPU 和 import.Memory 语法进行引用。

完整代码如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
syntax = "proto3";
option go_package = "protobuf/import;proto";
package import;
import "protobuf/import/component.proto";

message Computer {
  string name = 1;
  import.CPU cpu = 2;
  import.Memory memory = 3;
}

导入 compoent.proto 文件,这个也是相对路径,具体和 protoc –proto_path 组合起来才是完整路径。

一般指定为项目根目录的次一级目录,编译的时候直接在根目录编译。

protoc 编译的时候通过 --proto_path 指定在哪个目录去寻找 import 指定的文件。

比如指定 --proto_path=.即表示在当前目录下去寻找protobuf/import/compoent.proto这个文件。

3. 完整例子

目录结构如下

1
2
3
4
5
6
7
lixd@17x:~/17x/projects/grpc-go-example$ tree
├── protobuf
│   │ 
│   └── import
│       ├── compoent.proto
│       └── computer.proto
└── README.md

component.proto

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
syntax = "proto3";
option go_package = "protobuf/import;proto";
package import;

message CPU {
  string Name = 1;
  int64 Frequency = 2;
}
message Memory {
  string Name = 1;
  int64 Cap = 2;
}

computer.proto

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
syntax = "proto3";
option go_package = "protobuf/import;proto";
package import;
import "protobuf/import/component.proto";

message Computer {
  string name = 1;
  import.CPU cpu = 2;
  import.Memory memory = 3;
}

protoc 编译

项目根路径(grpc-go-example)下进行编译

1
lixd@17x:~/17x/projects/grpc-go-example$ protoc --proto_path=. --go_out=. ./protobuf/import/*.proto 

参数详解:

1)–proto_path=.

指定在当前目录( grpc-go-example)寻找 import 的文件(默认值也是当前目录)

然后 protobuf 文件中的 import 路径如下

1
import "protobuf/import/component.proto";

所以最终会去找 grpc-go-example/protobuf/import/component.proto

--proto_pathimport是可以互相调整的,只需要能找到就行。

建议protoc参数 –proto_path 指定为根目录,proto文件中的import 则从根目录次一级目录开始。

2)–go_out=.

指定将生成文件放在当前目录( grpc-go-example),同时因为 proto 文件中也指定了目录为protobuf/import,具体如下:

1
option go_package = "protobuf/import;proto";

所以最终生成目录为--go_out+go_package= grpc-go-example/protobuf/import

可以通过参数 --go_opt=paths=source_relative 来指定使用绝对路径,从而忽略掉 proto 文件中的 go_package 路径,直接生成在 –go_out 指定的路径。

3)./protobuf/import/*.proto

指定编译 import 目录下的所有 proto 文件,由于有文件的引入所以需要一起编译才能生效。

当然也可以一个一个编译,只要把相关文件都编译好即可。

Test

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
func Print() {
	c := Computer{
		Name: "alienware",
		Cpu: &CPU{
			Name:      "intel",
			Frequency: 4096,
		},
		Memory: &Memory{
			Name: "芝奇",
			Cap:  8192,
		},
	}
	fmt.Println(c.String())
}

4. 小结

  • 1)通过import "{path}"; 命令引入;
  • 2)导入后通过被导入文件包名.结构体名方式使用;
  • 3)编译时通过--proto_path=. 指定寻找proto文件的目录,一起编译。