proto3 语言向导链接:
Proto3 Language Guide
重要提示(2021-12-12 新增:)
本次环境搭建的相关版本如下:
protoc-3.19.1-win64
google.golang.org/grpc v1.42.0
如果版本不一致可能会导致奇奇怪怪的错误,不过作为一个合格的程序员,遇到问题应该能自己解决(版本问题一般可以通过官方文档,时间较新的技术文章)。
搭建准备(windows)
- 安装 protoc 编译器
protoc 点击进入下载地址
进入之后,找到最新的protoc-*-win64.zip下载即可。下载完之后,解压,然后将里面的 bin 目录添加到系统环境变量下即可。 - 安装 protoc 插件(前提是正确的搭建了 go 语言开发环境,并配备了 $GOPATH 环境变量)
go get -u google.golang.org/protobuf/cmd/protoc-gen-go
go install google.golang.org/protobuf/cmd/protoc-gen-go
在 $GOPATH 环境路径下找到刚刚执行的命令安装好的文件,我这里是下面这样的:
复制 protoc-gen-go.exe 到第 1 步里解压的目录下的 bin 目录下,我这里是下面这样的:
- 继续安装 protoc 插件(2021-12-12 新增:)
2021-12-12 新增:
go get -u google.golang.org/grpc/cmd/protoc-gen-go-grpc
go install google.golang.org/grpc/cmd/protoc-gen-go-grpc
2021-12-12 新增:
2021-12-12 新增:
在 $GOPATH 环境路径下找到刚刚执行的命令安装好的文件,我这里现在是下面这样的:
一、创建一个 proto 文件
文件名:helloworld.proto
syntax = "proto3";
option go_package = "proto/helloworld";
service Greeter {
rpc SayHello (HelloRequest) returns (HelloReply) {}
}
message HelloRequest {
string name = 1;
}
message HelloReply {
string message = 1;
}
- 文件的第一行指定你正在使用proto3语法:如果你不这样做,协议缓冲区编译器将假定你正在使用proto2。这必须是文件的第一个非空、非注释行。
- 文件的第三行 go_package 选项定义包的导入路径,该路径将包含为该文件生成的所有代码。Go包名将是导入路径的最后一个路径组件。
二、生成 proto 文件
[注意] 在项目的根目录下,执行 protoc 的相关命令,生成对应的 pd.go 文件,错误的命令如下:
protoc --go_out=plugins=grpc:. ./proto/*.proto
执行之后将会出现如下错误
E:\v4_workspace_golang\project_protoc>protoc --go_out=plugins=grpc:. ./proto/*proto
protoc-gen-go: unable to determine Go import path for "proto/helloworld.proto"
Please specify either:
• a "go_package" option in the .proto source file, or
• a "M" argument on the command line.
See https://developers.google.com/protocol-buffers/docs/reference/go-generated#package for more information.
--go_out: protoc-gen-go: Plugin failed with status code 1.
可能是版本的原因吧,总之别那么执行就好,参照官方文档应执行下述正确的命令:
protoc -I=. --go_out=. ./proto/*.proto
命令的定义是:
protoc -I=$SRC_DIR --go_out=$DST_DIR $SRC_DIR/*.proto
SRC_DIR(应用程序源代码所在的目录——如果不提供值,则使用当前目录),
DST_DIR(生成的代码要去的目录;通常与$SRC_DIR相同),以及.proto的路径。
执行完命令之后,在命令里指定的文件夹路径下将会生成对应的 helloworld.pb.go 文件
2021-12-12 新增
在我使用的版本里需要再次执行下面的命令:
protoc -I=. --go-grpc_out=. ./proto/*.proto
这里做个简单的说明:
我搭建环境使用的是最新的版本,和以前的版本相比,这个版本的 Service 需要单独使用 protoc-gen-go-grpc 插件,额外执行一次命令才能执行。
执行完命令之后,在命令里指定的文件夹路径下将会生成对应的 helloworld_grpc.pb.go 文件
三、简单看下 .pb.go 文件
type HelloRequest struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
}
func (x *HelloRequest) Reset() {
*x = HelloRequest{}
if protoimpl.UnsafeEnabled {
mi := &file_proto_helloworld_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *HelloRequest) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*HelloRequest) ProtoMessage() {}
func (x *HelloRequest) ProtoReflect() protoreflect.Message {
mi := &file_proto_helloworld_proto_msgTypes[0]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use HelloRequest.ProtoReflect.Descriptor instead.
func (*HelloRequest) Descriptor() ([]byte, []int) {
return file_proto_helloworld_proto_rawDescGZIP(), []int{0}
}
func (x *HelloRequest) GetName() string {
if x != nil {
return x.Name
}
return ""
}
...
在上述代码中,主要涉及 HelloRequest 类型,其包含了一组 Getters 方法,能够提供便捷的取值方式,并且处理一些空指针取值的情况,还能通过 Reset 方法来重置该参数。而该方法通过实现 ProtoMessage 方法,以表示这是一个实现了 proto.Message 的接口。
func (*HelloRequest) Descriptor() ([]byte, []int) {
return file_proto_helloworld_proto_rawDescGZIP(), []int{0}
}
func (*HelloReply) Descriptor() ([]byte, []int) {
return file_proto_helloworld_proto_rawDescGZIP(), []int{1}
}
每一个 Message Type 中都包含 Descriptor 方法。Descriptor 方法指对一个消息体定义的描述,而这个方法会在 file_proto_*_proto_rawDescGZIP 中寻找对应消息体的字段(Message Field) 所在的位置后再进行返回。
四、小节
本文介绍了 Protobuf 的使用方法。proto 文件需要通过 Protobuf 的编译器 protoc 来编译后才能使用,而在各个语言的具体插件实现中,protoc-gen-go 插件是针对 Go 语言的 protoc plugin,他们是相对隔离且解耦的。未来我们可以实现一个 protoc plugin,针对企业内部的定制化需求,非常的方便。
一些从书上看的命令,实际操作时还是出错了,最终解决还是靠的官方文档。书上学习、看技术博客学习都是不错的途径,不过都有一定的滞后性,做一个软件开发者,最好的资料还是官方文档,权威、靠谱。