feat: Support for multiple rpc service generation and rpc grouping (#1972)

* Add group & compatible flag

* Add group & compatible flag

* Support for multiple rpc service generation and rpc grouping

* Support for multiple rpc service generation and rpc grouping

* Format code

* Format code

* Add comments

* Fix unit test

* Refactor function name

* Add example & Update grpc readme

* go mod tidy

* update mod

* update mod
This commit is contained in:
anqiansong
2022-07-21 12:47:46 +08:00
committed by GitHub
parent 1b51d0ce82
commit ca3c687f1c
47 changed files with 2305 additions and 377 deletions

View File

@@ -7,7 +7,7 @@ Goctl Rpc是`goctl`脚手架下的一个rpc服务代码生成模块支持prot
* 简单易用
* 快速提升开发效率
* 出错率低
* 贴近protoc
* 贴近 protoc
## 快速开始
@@ -24,168 +24,41 @@ Goctl Rpc是`goctl`脚手架下的一个rpc服务代码生成模块支持prot
执行后代码结构如下:
```golang
```text
.
── etc // yaml配置文件
── greet.yaml
├── go.mod
├── greet // pb.go文件夹①
│ └── greet.pb.go
├── greet.go // main函数
├── greet.proto // proto 文件
├── greetclient // call logic ②
── greet.go
└── internal
├── config // yaml配置对应的实体
└── config.go
├── logic // 业务代码
└── pinglogic.go
├── server // rpc server
└── greetserver.go
└── svc // 依赖资源
└── servicecontext.go
```
> ① pb文件夹名老版本文件夹固定为pb称取自于proto文件中option go_package的值最后一层级按照一定格式进行转换若无此声明则取自于package的值大致代码如下
```go
if option.Name == "go_package" {
ret.GoPackage = option.Constant.Source
}
...
if len(ret.GoPackage) == 0 {
ret.GoPackage = ret.Package.Name
}
ret.PbPackage = GoSanitized(filepath.Base(ret.GoPackage))
...
── greet
── etc
│   └── greet.yaml
├── greet
│   ├── greet.go
│   ├── greet.pb.go
│   └── greet_grpc.pb.go
├── greet.go
── greet.proto
└── internal
├── config
│   └── config.go
├── logic
│   └── pinglogic.go
├── server
│   └── greetserver.go
└── svc
└── servicecontext.go
```
> GoSanitized方法请参考google.golang.org/protobuf@v1.25.0/internal/strs/strings.go:71
> ② call 层文件夹名称取自于proto中service的名称如该sercice的名称和pb文件夹名称相等则会在srervice后面补充client进行区分使pb和call分隔。
```go
if strings.ToLower(proto.Service.Name) == strings.ToLower(proto.GoPackage) {
callDir = filepath.Join(ctx.WorkDir, strings.ToLower(stringx.From(proto.Service.Name+"_client").ToCamel()))
}
```
rpc一键生成常见问题解决见 <a href="#常见问题解决">常见问题解决</a>
### 方式二通过指定proto生成rpc服务
* 生成proto模板
```Bash
goctl rpc template -o=user.proto
```
```golang
syntax = "proto3";
package remote;
message Request {
// 用户名
string username = 1;
// 用户密码
string password = 2;
}
message Response {
// 用户名称
string name = 1;
// 用户性别
string gender = 2;
}
service User {
// 登录
rpc Login(Request)returns(Response);
}
```
* 生成rpc服务代码
```Bash
goctl rpc proto -src=user.proto
```
## 准备工作
* 安装了go环境
* 安装了protoc&protoc-gen-go并且已经设置环境变量
* 更多问题请见 <a href="#注意事项">注意事项</a>
## 用法
### rpc服务生成用法
```Bash
goctl rpc proto -h
$ goctl rpc template -o=user.proto
```
```Bash
NAME:
goctl rpc proto - generate rpc from proto
USAGE:
goctl rpc proto [command options] [arguments...]
OPTIONS:
--src value, -s value the file path of the proto source file
--proto_path value, -I value native command of protoc, specify the directory in which to search for imports. [optional]
--go_opt value native command of protoc-gen-go, specify the mapping from proto to go, eg --go_opt=proto_import=go_package_import. [optional]
--dir value, -d value the target path of the code
--style value the file naming format, see [https://github.com/zeromicro/go-zero/tree/master/tools/goctl/config/readme.md]
--idea whether the command execution environment is from idea plugin. [optional]
```
### 参数说明
* --src 必填proto数据源目前暂时支持单个proto文件生成
* --proto_path 可选protoc原生子命令用于指定proto import从何处查找可指定多个路径,如`goctl rpc -I={path1} -I={path2} ...`
,在没有import时可不填。当前proto路径不用指定已经内置`-I`的详细用法请参考`protoc -h`
* --go_opt 可选protoc-gen-go插件原生flag用于指定go_package
* --dir 可选默认为proto文件所在目录生成代码的目标目录
* --style 可选,指定生成文件名的命名风格
* --idea 可选是否为idea插件中执行终端执行可以忽略
### 开发人员需要做什么
关注业务代码编写将重复性、与业务无关的工作交给goctl生成好rpc服务代码后开发人员仅需要修改
* 服务中的配置文件编写(etc/xx.json、internal/config/config.go)
* 服务中业务逻辑编写(internal/logic/xxlogic.go)
* 服务中资源上下文的编写(internal/svc/servicecontext.go)
### 注意事项
* proto不支持暂多文件同时生成
* proto不支持外部依赖包引入message不支持inline
* 目前main文件、shared文件、handler文件会被强制覆盖而和开发人员手动需要编写的则不会覆盖生成这一类在代码头部均有
```shell script
// Code generated by goctl. DO NOT EDIT!
// Source: xxx.proto
```
的标识,请注意不要将也写业务性代码写在里面。
## proto import
* 对于rpc中的requestType和returnType必须在main proto文件定义对于proto中的message可以像protoc一样import其他proto文件。
proto示例:
### 错误import
```proto
syntax = "proto3";
package greet;
import "base/common.proto"
package user;
option go_package="./user";
message Request {
string ping = 1;
@@ -195,40 +68,136 @@ message Response {
string pong = 1;
}
service Greet {
rpc Ping(base.In) returns(base.Out);// request和return 不支持import
}
```
### 正确import
```proto
syntax = "proto3";
package greet;
import "base/common.proto"
message Request {
base.In in = 1;// 支持import
}
message Response {
base.Out out = 2;// 支持import
}
service Greet {
service User {
rpc Ping(Request) returns(Response);
}
```
## 常见问题解决(go mod工程)
* 生成rpc服务代码
* 错误一:
```text
A required privilege is not held by the client.
```bash
$ goctl rpc protoc user.proto --go_out=. --go-grpc_out=. --zrpc_out=.
```
这个问题只有goctl 版本在`goctl.exe version 1.2.1` 以后的 Windows操作系统出现主要原因是goctl需要以管理员身份运行这样才能将`goctl.exe` 创建一个 `ptocot-gen-gcotl`
的符号链接。
## 用法
### rpc 服务生成用法
```Bash
$ goctl rpc protoc -h
Generate grpc code
Usage:
goctl rpc protoc [flags]
Examples:
goctl rpc protoc xx.proto --go_out=./pb --go-grpc_out=./pb --zrpc_out=.
Flags:
--branch string The branch of the remote repo, it does work with --remote
-h, --help help for protoc
--home string The goctl home path of the template, --home and --remote cannot be set at the same time, if they are, --remote has higher priority
-m, --multiple Generated in multiple rpc service mode
--remote string The remote git repo of the template, --home and --remote cannot be set at the same time, if they are, --remote has higher priority
The git repo directory must be consistent with the https://github.com/zeromicro/go-zero-template directory structure
--style string The file naming format, see [https://github.com/zeromicro/go-zero/tree/master/tools/goctl/config/readme.md] (default "gozero")
-v, --verbose Enable log output
--zrpc_out string The zrpc output directory
```
### 参数说明
* --branch 指定远程仓库模板分支
* --home 指定goctl模板根目录
* -m, --multiple 指定生成多个rpc服务模式, 默认为 false, 如果为 false, 则只支持生成一个rpc service, 如果为 true, 则支持生成多个 rpc service且多个 rpc service 会分组。
* --style 指定文件输出格式
* -v, --verbose 显示日志
* --zrpc_out 指定zrpc输出目录
> ## --multiple
> 是否开启多个 rpc service 生成,如果开启,则满足一下新特性
> 1. 支持 1 到多个 rpc service
> 2. 生成 rpc 服务会按照服务名称分组(尽管只有一个 rpc service
> 3. rpc client 的文件目录变更为固定名称 `client`
>
> 如果不开启,则和旧版本 rpc 生成逻辑一样(兼容)
> 1. 有且只能有一个 rpc service
## rpc 服务生成 example
详情见 [example/rpc](https://github.com/zeromicro/go-zero/tree/master/tools/goctl/example)
## --multiple 为 true 和 false 的目录区别
源 proto 文件
```protobuf
syntax = "proto3";
package hello;
option go_package = "./hello";
message HelloReq {
string in = 1;
}
message HelloResp {
string msg = 1;
}
service Greet {
rpc SayHello(HelloReq) returns (HelloResp);
}
```
### --multiple=true
```text
hello
├── client // 区别1rpc client 目录固定为 client 名称
│   └── greet // 区别2会按照 rpc service 名称分组
│   └── greet.go
├── etc
│   └── hello.yaml
├── hello.go
├── internal
│   ├── config
│   │   └── config.go
│   ├── logic
│   │   └── greet // 区别2会按照 rpc service 名称分组
│   │   └── sayhellologic.go
│   ├── server
│   │   └── greet // 区别2会按照 rpc service 名称分组
│   │   └── greetserver.go
│   └── svc
│   └── servicecontext.go
└── pb
└── hello
├── hello.pb.go
└── hello_grpc.pb.go
```
### --multiple=false (旧版本目录,向后兼容)
```text
hello
├── etc
│   └── hello.yaml
├── greet
│   └── greet.go
├── hello.go
├── internal
│   ├── config
│   │   └── config.go
│   ├── logic
│   │   └── sayhellologic.go
│   ├── server
│   │   └── greetserver.go
│   └── svc
│   └── servicecontext.go
└── pb
└── hello
├── hello.pb.go
└── hello_grpc.pb.go
```