概念
proto 中的go_package 的作用?
在 Protocol Buffers 中,go_package 是一个可选的选项,用于指定生成的 Go 代码的包名和导入路径。它告诉编译器将生成的 Go 代码放在哪个包中,并且在需要时为该包添加必要的导入语句。
具体来说,go_package 的值应该是形如 “<import_path>/<package_name>” 的字符串,其中 import_path 是所生成的 Go 代码的导入路径,而 package_name 则是代码所属的包名。例如:
1
2
3
4
5
6
7
8 >syntax = "proto3";
>package mypackage;
>option go_package = "example.com/proto/mypackage;mypackage";
>
>message MyMessage {
> // ...
>}
>
这里指定了生成的 Go 代码应该位于 example.com/proto/mypackage 包中,并且该包的名称为 mypackage。当编译器生成代码时,它会自动将生成的代码保存到正确的目录,并在需要时添加
import "example.com/proto/mypackage"
语句。
go_package 字段 和 package 字段的关系和区别?
在 Protocol Buffers 中,package 字段用于指定当前 .proto 文件中定义的所有消息和服务所属的包名。而 go_package 字段则是针对生成的 Go 代码的一个特殊选项,用于指定生成的 Go 代码的包名和导入路径。
当一个 .proto 文件中定义了多个消息或服务时,它们都应该属于同一个包,这时就需要使用 package 字段来指定它们所属的包名,例如:
1
2
3
4
5
6
7
8
9
10
11 >syntax = "proto3";
>package mypackage;
>
>message MyMessage {
> // ...
>}
>
>service MyService {
> // ...
>}
>
这里指定了 MyMessage 和 MyService 都属于 mypackage 包。在生成 Go 代码时,Protoc 编译器会自动将这些消息和服务保存到正确的目录结构中,并按照 Go 语言的规范将它们组织为一个 package。
而 go_package 字段则是可选的,用于指定生成的 Go 代码所在的包名和导入路径。这个选项通常用于控制生成的 Go 代码文件的导入路径,以及生成的包的名称。例如:
1
2
3
4
5
6
7
8 >syntax = "proto3";
>package mypackage;
>option go_package = "example.com/proto/mypackage;mypackage";
>
>message MyMessage {
> // ...
>}
>
这里指定了生成的 Go 代码应该位于 example.com/proto/mypackage 包中,并且该包的名称为 mypackage。在执行 protoc 命令时,编译器会将生成的 Go 代码存储到指定的目录下,并修改生成的代码中的 import 语句,以便正确引用其他包。
–go_out参数 和 proto 文件中的 go_package 有什么联系和区别?
--go_out
参数和go_package
选项都是与生成Go代码相关的选项,但它们的作用不同。
--go_out
参数是protoc编译器的一个选项,用于指定生成的Go文件输出的目录和前缀。可以使用该选项来自定义生成的代码的输出位置和名称,同时还可以使用--go_opt
选项来设置其他选项,例如输出路径为相对路径等。例如:
1
2 > protoc --go_out=./generated --go_opt=paths=source_relative ./proto/example.proto
>
上面的命令将会把生成的Go代码输出到
./generated
目录下,并且设置包名使用相对路径。而
go_package
选项则是在.proto
文件中定义的一个选项,用于指定生成的Go代码所在的包名。这个选项中的值必须是一个合法的Go包名(就像在Go源文件中一样),并且需要和--go_out
参数中指定的目录结构匹配。如果没有指定go_package
选项,则默认情况下,生成的Go代码的包名将会是.proto
文件中的package
语句的值。例如:
1
2
3
4
5
6
7
8
9 > syntax = "proto3";
> package mypackage;
>
> option go_package = "github.com/myusername/mypackage/proto";
>
> message MyMessage {
> // ...
> }
>
上面的例子中,
go_package
选项指定了生成的Go代码所在的包名为github.com/myusername/mypackage/proto
。然后,使用--go_out
参数指定输出目录为./generated
,那么编译器将会把生成的Go代码输出到./generated/github.com/myusername/mypackage/proto
目录下。所以最终生成文件的目录 = –go_out + go_package 的值。
举例:protoc 命令为 protoc –proto_path=pb –go_out=pb –go_opt=paths=source_relative book/price.proto, 文件中 option go_package=”proto_demo/pb/book”; 最终生成的文件会在哪个目录,为什么?
执行完该命令后生成的文件会位于
proto_demo/pb/book
目录下,文件名为price.pb.go
。这是因为在proto文件中指定了go_package选项值为”proto_demo/pb/book”,该选项会告诉编译器生成的Go代码的包名为”book”,并且使用相对于生成代码文件所在目录的路径作为包名。而在命令行中,
--go_out=pb
参数指定生成的Go代码文件应该输出到当前目录下的pb
子目录中,--go_opt=paths=source_relative
参数指定生成的Go代码中的导入路径使用相对路径,也就是相对于生成代码文件所在目录。因此,最终生成的文件会位于
proto_demo/pb/book/price.pb.go
路径下。
举例:protoc 命令为 protoc –proto_path=pb –go_out=generated –go_opt=paths=source_relative book/price.proto, 文件中 option go_package=”proto_demo/pb/book”; 最终生成的文件会在哪个目录,为什么?
protoc
命令会将pb
目录下的price.proto
文件编译成 Go 语言的源代码,并将生成的代码放置在generated
目录中。其中,--proto_path=pb
参数指定了查找.proto
文件的目录为pb
目录,--go_out=generated
参数指定了输出目录为generated
目录,而--go_opt=paths=source_relative
参数则是指定生成的 Go 源文件中的包路径使用相对路径。在
price.proto
文件中,option go_package="proto_demo/pb/book";
指定了生成的 Go 源代码的包路径为proto_demo/pb/book
。因此,最终生成的文件路径应该为generated/proto_demo/pb/book/price.pb.go
。其中,proto_demo
和pb
目录都是根据go_package
中的值来确定的,而price.pb.go
则是根据原始的.proto
文件名生成的。
引用同包下的 proto
文件目录结构如下,在 proto_demo 项目下,新建一个 pb 文件夹专门存放 proto 文件。
1 | |____proto_demo |
price.proto 的内容如下
1 | syntax = "proto3"; |
book.proto 的内容如下
1 | syntax = "proto3"; |
在 book.proto 中引用了 price.proto 中的字段,因为 price.proto 和 book.proto 都是 package book
包,所以是同一个包,属于相同包的引用。这时只需要从起始位置为 proto_path 参数的下层开始导入即可。而且导入的 Price 字段前面不需要添加包名。
引用不同包下的 proto
author.proto 的内容如下
1 | syntax = "proto3"; |
author.proto 属于 author 包,在 book中导入 author 的时候属于跨包导入,所以在 book 中导入字段需要添加包名,然后 . 字段名。例如上面的 author.Info author= 3;
引用谷歌包下的 proto
上面的 book 中引入了 google 的 Timestamp,前提是安装 Protocol Buffers 的时候将 include 文件夹也保留了,并且将 bin 下面的 protoc 添加到了环境变量。
1 | protoc |
protoc-22.2-osx-x86_64 目录下的 include 就含有谷歌内置的 proto 文件。
生成 go 代码
1 | protoc --proto_path=pb --go_out=pb --go_opt=paths=source_relative book/price.proto book/book.proto author/author.proto |
上述命令在 proto_demo 目录下执行
生成 grpc 的代码
定义了 service 才会生成 grpc 的代码
1 | protoc --proto_path=pb --go-grpc_out=pb --go-grpc_opt=paths=source_relative book/book.proto book/price.proto author/author.proto |
上述命令在 proto_demo 目录下执行
使用 makefile
在 proto_demo 下新增 makefile 文件,内容如下,其中需要修改 PROTO_DIR 为自己的 proto 目录,这里为 pb
1 |
|
解决 Goland Proto 文件导入提出找不到问题
在下面文件中,在 message Book 中,分别引入了同包的 proto 文件和 不同包的 proto 文件。但默认 goland 会提示不存在。解决方法是在 goland 的设置中 语言和框架 Protocol Buffers,不勾选 Configure automatically, 并在下面新增一个存放 –proto_path 参数的值,例如这里的 pb。
1 | syntax = "proto3"; |
参考文章:https://www.liwenzhou.com/posts/Go/protobuf/
本文代码:https://github.com/rexyan/Go-Microservice/tree/main/proto_demo