You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

9.3 KiB

Protobuf 规范 概述 ​ 本教程使用 Protocol Buffers 语言的 proto3 版本,为 Go 程序员提供了使用 Protocol Buffers 的基本介绍。通过创建一个简单的示例应用程序,它向您展示了如何

在 .proto 文件中定义消息格式。 使用协议缓冲区编译器。 使用 Go protocol buffer API 来写入和读取消息。

服务分组 概述 ​ go-zero 采用 gRPC 进行服务间的通信,我们通过 proto 文件来定义服务的接口,但是在实际的开发中,我们可能会有多个服务,如果不对服务进行文件分组,那么 goctl 生成的代码将会是一个大的文件夹,这样会导致代码的可维护性变差,因此服务分组可以提高代码的可读性和可维护性。

服务分组 ​ 在 go-zero 中,我们通过在 proto 文件中以 service 为维度来进行文件分组,我们可以在 proto 文件中定义多个 service,每个 service 都会生成一个独立的文件夹,这样就可以将不同的服务进行分组,从而提高代码的可读性和可维护性。

除了 proto 文件中定义了 service 外,分组与否还需要在 goctl 中控制,生成带分组或者不带分组的代码取决于开发者,我们通过示例来演示一下。

不带分组 ​ 假设我们有一个 proto 文件,如下:

syntax = "proto3";

package user;

option go_package = "github.com/example/user";

message LoginReq{} message LoginResp{} message UserInfoReq{} message UserInfoResp{} message UserInfoUpdateReq{} message UserInfoUpdateResp{} message UserListReq{} message UserListResp{}

message UserRoleListReq{} message UserRoleListResp{} message UserRoleUpdateReq{} message UserRoleUpdateResp{} message UserRoleInfoReq{} message UserRoleInfoResp{} message UserRoleAddReq{} message UserRoleAddResp{} message UserRoleDeleteReq{} message UserRoleDeleteResp{}

message UserClassListReq{} message UserClassListResp{} message UserClassUpdateReq{} message UserClassUpdateResp{} message UserClassInfoReq{} message UserClassInfoResp{} message UserClassAddReq{} message UserClassAddResp{} message UserClassDeleteReq{} message UserClassDeleteResp{}

service UserService{ rpc Login (LoginReq) returns (LoginResp); rpc UserInfo (UserInfoReq) returns (UserInfoResp); rpc UserInfoUpdate (UserInfoUpdateReq) returns (UserInfoUpdateResp); rpc UserList (UserListReq) returns (UserListResp);

rpc UserRoleList (UserRoleListReq) returns (UserRoleListResp); rpc UserRoleUpdate (UserRoleUpdateReq) returns (UserRoleUpdateResp); rpc UserRoleInfo (UserRoleInfoReq) returns (UserRoleInfoResp); rpc UserRoleAdd (UserRoleAddReq) returns (UserRoleAddResp); rpc UserRoleDelete (UserRoleDeleteReq) returns (UserRoleDeleteResp);

rpc UserClassList (UserClassListReq) returns (UserClassListResp); rpc UserClassUpdate (UserClassUpdateReq) returns (UserClassUpdateResp); rpc UserClassInfo (UserClassInfoReq) returns (UserClassInfoResp); rpc UserClassAdd (UserClassAddReq) returns (UserClassAddResp); rpc UserClassDelete (UserClassDeleteReq) returns (UserClassDeleteResp); } 我们来看一下不分组的情况下,goctl 生成的代码结构:

$ goctl rpc protoc user.proto --go_out=. --go-grpc_out=. --zrpc_out=. $ tree . ├── etc │ └── user.yaml ├── github.com │ └── example │ └── user │ ├── user.pb.go │ └── user_grpc.pb.go ├── go.mod ├── internal │ ├── config │ │ └── config.go │ ├── logic │ │ ├── loginlogic.go │ │ ├── userclassaddlogic.go │ │ ├── userclassdeletelogic.go │ │ ├── userclassinfologic.go │ │ ├── userclasslistlogic.go │ │ ├── userclassupdatelogic.go │ │ ├── userinfologic.go │ │ ├── userinfoupdatelogic.go │ │ ├── userlistlogic.go │ │ ├── userroleaddlogic.go │ │ ├── userroledeletelogic.go │ │ ├── userroleinfologic.go │ │ ├── userrolelistlogic.go │ │ └── userroleupdatelogic.go │ ├── server │ │ └── userserviceserver.go │ └── svc │ └── servicecontext.go ├── user.go ├── user.proto └── userservice └── userservice.go

10 directories, 24 files 温馨提示 在不进行分组的情况下,不支持在 proto 文件中定义多个 service,否则会报错。

带分组 ​ 首先,我们需要在 proto 文件中定义多个 service,如下:

syntax = "proto3";

package user;

option go_package = "github.com/example/user";

message LoginReq{} message LoginResp{} message UserInfoReq{} message UserInfoResp{} message UserInfoUpdateReq{} message UserInfoUpdateResp{} message UserListReq{} message UserListResp{} service UserService{ rpc Login (LoginReq) returns (LoginResp); rpc UserInfo (UserInfoReq) returns (UserInfoResp); rpc UserInfoUpdate (UserInfoUpdateReq) returns (UserInfoUpdateResp); rpc UserList (UserListReq) returns (UserListResp); }

message UserRoleListReq{} message UserRoleListResp{} message UserRoleUpdateReq{} message UserRoleUpdateResp{} message UserRoleInfoReq{} message UserRoleInfoResp{} message UserRoleAddReq{} message UserRoleAddResp{} message UserRoleDeleteReq{} message UserRoleDeleteResp{} service UserRoleService{ rpc UserRoleList (UserRoleListReq) returns (UserRoleListResp); rpc UserRoleUpdate (UserRoleUpdateReq) returns (UserRoleUpdateResp); rpc UserRoleInfo (UserRoleInfoReq) returns (UserRoleInfoResp); rpc UserRoleAdd (UserRoleAddReq) returns (UserRoleAddResp); rpc UserRoleDelete (UserRoleDeleteReq) returns (UserRoleDeleteResp); }

message UserClassListReq{} message UserClassListResp{} message UserClassUpdateReq{} message UserClassUpdateResp{} message UserClassInfoReq{} message UserClassInfoResp{} message UserClassAddReq{} message UserClassAddResp{} message UserClassDeleteReq{} message UserClassDeleteResp{} service UserClassService{ rpc UserClassList (UserClassListReq) returns (UserClassListResp); rpc UserClassUpdate (UserClassUpdateReq) returns (UserClassUpdateResp); rpc UserClassInfo (UserClassInfoReq) returns (UserClassInfoResp); rpc UserClassAdd (UserClassAddReq) returns (UserClassAddResp); rpc UserClassDelete (UserClassDeleteReq) returns (UserClassDeleteResp); } 我们来看一下带分组的情况下,goctl 生成的代码结构:

通过 -m 指定 goctl 生成分组的代码

$ goctl rpc protoc user.proto --go_out=. --go-grpc_out=. --zrpc_out=. -m $ tree . ├── client │ ├── userclassservice │ │ └── userclassservice.go │ ├── userroleservice │ │ └── userroleservice.go │ └── userservice │ └── userservice.go ├── etc │ └── user.yaml ├── github.com │ └── example │ └── user │ ├── user.pb.go │ └── user_grpc.pb.go ├── go.mod ├── internal │ ├── config │ │ └── config.go │ ├── logic │ │ ├── userclassservice │ │ │ ├── userclassaddlogic.go │ │ │ ├── userclassdeletelogic.go │ │ │ ├── userclassinfologic.go │ │ │ ├── userclasslistlogic.go │ │ │ └── userclassupdatelogic.go │ │ ├── userroleservice │ │ │ ├── userroleaddlogic.go │ │ │ ├── userroledeletelogic.go │ │ │ ├── userroleinfologic.go │ │ │ ├── userrolelistlogic.go │ │ │ └── userroleupdatelogic.go │ │ └── userservice │ │ ├── loginlogic.go │ │ ├── userinfologic.go │ │ ├── userinfoupdatelogic.go │ │ └── userlistlogic.go │ ├── server │ │ ├── userclassservice │ │ │ └── userclassserviceserver.go │ │ ├── userroleservice │ │ │ └── userroleserviceserver.go │ │ └── userservice │ │ └── userserviceserver.go │ └── svc │ └── servicecontext.go ├── user.go └── user.proto

19 directories, 28 files 通过目录结构我们可以看出,logic、server、client 目录都会根据 service 进行分组。

  1. goctl 生成 gRPC 代码时 proto 使用规范 ​ 在使用 goctl 生成 gRPC 代码时,编写的所有 rpc 方法的请求体和响应体必须在主 proto 中声明 message,即不支持从外包外 message,以下是 不支持 的 import 示例: syntax = "proto3";

package greet;

import "base.proto"

service demo{ rpc (base.DemoReq) returns (base.DemoResp); } 正确写法

syntax = "proto3";

package greet;

message DemoReq{} message DemoResp{}

service demo{ rpc (DemoReq) returns (DemoResp); } 在满足 1 的情况,proto import 只支持 message 引入,不支持 service 引入。 2. 为什么使用 goctl 生成 gRPC 代码时 proto 不支持使用包外 proto 和 service?​ 对于包外的 proto,goctl 没法完全控制,有几个问题 goctl 没法解决:

包外的 proto 如果层级很深,亦或在其他公共仓库中,goctl 没法确定包外 proto 是否已经生成了 pb.go 如果包外的 proto 生成了 pb.go,那些 pb.go 生成在哪些目录下,是否在同一个工程中,如果在当前工程外,pb.go 所在工程是否使用了 go module 等等,goctl 没法得知,因此没法定位到其真正的 go package。 如果包外的 proto 没有生成,既然其属于公共 proto,那么 goctl 是否需要针对每次引入都要生成,这个也无从解决 3. goctl 生成 gRPC 不支持 google/protobuf/empty.proto 包的 import​ 答案,同 1,2