You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
go-zero/tools/goctl/rpc
anqiansong 9f4a882a1b
fix issue #831 (#850)
* fix issue #831

* fix typo

Co-authored-by: anqiansong <anqiansong@xiaoheiban.cn>
3 years ago
..
cli fix issue #831 (#850) 3 years ago
execx chore(format): change by gofumpt tool (#697) 4 years ago
generator Fix the error stream method name (#826) 3 years ago
parser To generate grpc stream, fix issue #616 (#815) 3 years ago
README.md Add --go_opt flag to adapt to the version after 1.4.0 of protoc-gen-go (#767) 3 years ago

README.md

Rpc Generation

Goctl Rpc是goctl脚手架下的一个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的值大致代码如下

  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))
  ...

GoSanitized方法请参考google.golang.org/protobuf@v1.25.0/internal/strs/strings.go:71

② 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()))
}

rpc一键生成常见问题解决常见问题解决

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

  • 生成proto模板

    goctl rpc template -o=user.proto
    
    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服务代码

    goctl rpc proto -src=user.proto
    

准备工作

  • 安装了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]
   --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/tal-tech/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)

注意事项

  • google.golang.org/grpc需要降级到 v1.29.1且protoc-gen-go版本不能高于v1.3.2see https://github.com/grpc/grpc-go/issues/3347)即

    replace google.golang.org/grpc => google.golang.org/grpc v1.29.1
    
  • 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文件。

proto示例:

错误import

syntax = "proto3";

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

syntax = "proto3";

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);
}

常见问题解决(go mod工程)

  • 错误一:

    pb/xx.pb.go:220:7: undefined: grpc.ClientConnInterface
    pb/xx.pb.go:224:11: undefined: grpc.SupportPackageIsVersion6
    pb/xx.pb.go:234:5: undefined: grpc.ClientConnInterface
    pb/xx.pb.go:237:24: undefined: grpc.ClientConnInterface
    

    解决方法:请将protoc-gen-go版本降至v1.3.2及一下

  • 错误二:

    
    # go.etcd.io/etcd/clientv3/balancer/picker
    ../../../go/pkg/mod/go.etcd.io/etcd@v0.0.0-20200402134248-51bdeb39e698/clientv3/balancer/picker/err.go:25:9: cannot use &errPicker literal (type *errPicker) as type Picker in return argument:*errPicker does not implement Picker (wrong type for Pick method)
      have Pick(context.Context, balancer.PickInfo) (balancer.SubConn, func(balancer.DoneInfo), error)
      want Pick(balancer.PickInfo) (balancer.PickResult, error)
      ../../../go/pkg/mod/go.etcd.io/etcd@v0.0.0-20200402134248-51bdeb39e698/clientv3/balancer/picker/roundrobin_balanced.go:33:9: cannot use &rrBalanced literal (type *rrBalanced) as type Picker in return argument:
      *rrBalanced does not implement Picker (wrong type for Pick method)
      	have Pick(context.Context, balancer.PickInfo) (balancer.SubConn, func(balancer.DoneInfo), error)
      want Pick(balancer.PickInfo) (balancer.PickResult, error)
      #github.com/tal-tech/go-zero/zrpc/internal/balancer/p2c
      ../../../go/pkg/mod/github.com/tal-tech/go-zero@v1.0.12/zrpc/internal/balancer/p2c/p2c.go:41:32: not enough arguments in call to base.NewBalancerBuilder
      have (string, *p2cPickerBuilder)
    want (string, base.PickerBuilder, base.Config)
    ../../../go/pkg/mod/github.com/tal-tech/go-zero@v1.0.12/zrpc/internal/balancer/p2c/p2c.go:58:9: cannot use &p2cPicker literal (type *p2cPicker) as type balancer.Picker in return argument:
      *p2cPicker does not implement balancer.Picker (wrong type for Pick method)
      	have Pick(context.Context, balancer.PickInfo) (balancer.SubConn, func(balancer.DoneInfo), error)
      	want Pick(balancer.PickInfo) (balancer.PickResult, error)
    

    解决方法:

    replace google.golang.org/grpc => google.golang.org/grpc v1.29.1