跳到主要内容

构建RPC

goctl rpcgoctl脚手架下的一个rpc服务代码生成模块,支持proto模板生成和rpc服务代码生成,通过此工具生成代码你只需要关注业务逻辑编写而不用去编写一些重复性的代码。这使得我们把精力重心放在业务上,从而加快了开发效率且降低了代码出错率。

特性

  • 简单易用
  • 快速提升开发效率
  • 出错率低
  • 贴近protoc

快速开始

方式一:快速生成greet服务

通过命令 goctl rpc new ${servieName}生成

如生成greet rpc服务:

goctl rpc new greet

执行后代码结构如下:

.
├── 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的值,大致代码如下:

google.golang.org/protobuf@v1.25.0/internal/strs/strings.go:71
  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))'
...
提示

call 层文件夹名称取自于proto中service的名称,如该sercice的名称和pb文件夹名称相等,则会在srervice后面补充client进行区分,使pb和call分隔。

if strings.ToLower(proto.Service.Name) == strings.ToLower(proto.GoPackage) {
callDir = filepath.Join(ctx.WorkDir, strings.ToLower(stringx.From(proto.Service.Name+"_client").ToCamel()))
}

方式二:通过指定proto生成rpc服务

  • 生成proto模板

    goctl rpc template -o=user.proto
    user.proto
    syntax = "proto3";

    package remote;

    option go_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服务代码

    goctl rpc proto -src user.proto -dir .

准备工作

  • 安装了go环境
  • 安装了protoc & protoc-gen-go,并且已经设置环境变量
  • 更多问题请见 注意事项

用法

rpc服务生成用法

goctl rpc proto -h
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]
--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
  • --dir 可选,默认为proto文件所在目录,生成代码的目标目录
  • --style 可选,输出目录的文件命名风格,详情见https://github.com/zeromicro/go-zero/tree/master/tools/goctl/config/readme.md
  • --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文件会被强制覆盖,而和开发人员手动需要编写的则不会覆盖生成,这一类在代码头部均有

    // Code generated by goctl. DO NOT EDIT!
    // Source: xxx.proto

    的标识,请注意不要将也写业务性代码写在里面。

proto import

  • 对于rpc中的requestType和returnType必须在main proto文件定义,对于proto中的message可以像protoc一样import其他proto文件。

错误import

greet.proto
syntax = "proto3";

package greet;

option go_package = "greet";

import "base/common.proto"

message Request {
string ping = 1;
}

message Response {
string pong = 1;
}

service Greet {
rpc Ping(base.In) returns(base.Out);// request和return 不支持import
}

正确import

greet.proto
syntax = "proto3";

package greet;

option go_package = "greet";

import "base/common.proto"

message Request {
base.In in = 1;// 支持import
}

message Response {
base.Out out = 2;// 支持import
}

service Greet {
rpc Ping(Request) returns(Response);
}