151. 【go-语言】gRPC-环境搭建(二)

proto3 语言向导链接:
Proto3 Language Guide

重要提示(2021-12-12 新增:)

本次环境搭建的相关版本如下:
protoc-3.19.1-win64
google.golang.org/grpc v1.42.0
如果版本不一致可能会导致奇奇怪怪的错误,不过作为一个合格的程序员,遇到问题应该能自己解决(版本问题一般可以通过官方文档,时间较新的技术文章)。

搭建准备(windows)

  1. 安装 protoc 编译器
    protoc 点击进入下载地址
    进入之后,找到最新的protoc-*-win64.zip下载即可。下载完之后,解压,然后将里面的 bin 目录添加到系统环境变量下即可。
  2. 安装 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文件路径

复制 protoc-gen-go.exe 到第 1 步里解压的目录下的 bin 目录下,我这里是下面这样的:
复制 protoc-gen-go.exe 到第一步里的 bin 目录下
3. 继续安装 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 新增:
protoc-gen-go-grpc.exe 文件路径
2021-12-12 新增:
在 $GOPATH 环境路径下找到刚刚执行的命令安装好的文件,我这里现在是下面这样的:
复制 protoc-gen-go-grpc.exe 到第一步里的 bin 目录下

一、创建一个 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,针对企业内部的定制化需求,非常的方便。


一些从书上看的命令,实际操作时还是出错了,最终解决还是靠的官方文档。书上学习、看技术博客学习都是不错的途径,不过都有一定的滞后性,做一个软件开发者,最好的资料还是官方文档,权威、靠谱。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值