zeromicro / go-zero

A cloud-native Go microservices framework with cli tool for productivity.

Home Page:https://go-zero.dev

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

How to support protoc-gen-go better in goctl?

kevwan opened this issue · comments

Proposal

背景

为了帮助开发者提升开发效率,促进敏捷开发,减少错误的产生,我们基于 grpc + go-zero 官方推荐方案实现了 zrpc 的代码生成,从开源到现在,得到了广大开发者的一致好评,当然大家也反映了一些问题,我们很高兴能够收到大家的反馈,说明大家在真正使用这个工具,而且希望其能够帮助大家解决实际问题。

现状

当前 zrpc 代码生成逻辑是通过goctl 暴露 proto和输出目录参数进行 protoc 命令拼接,其最大的弊端是限制了用户使用 protoc 生成 pb 代码,且用户安装的 protoc-gen-go版本的不同,其带来的兼容性维护也是非常巨大的。

依赖

  • Protoc
  • Protoc-gen-go

实现逻辑

  • 环境检测,包括 golang、protoc、protoc-gen-go
  • Goctl 启动 protoc 进程生成 grpc 代码
  • Goctl 生成 server、client 实现代码

暴露问题

现有 grpc 代码生成逻辑是 goctl 内部执行 ,在 goctl 中使用的命令为

$ goctl rpc proto -src ${source.proto} --dir=${output_dir} [--import_path] [--go_opt]

其内部真正执行的命令为

$ protoc ${source.proto} --go_out=plugins=grpc:${output_dir} [--import_path] [--go_opt]

这个命令怎么来的?
WX20211117-103122@2x

从图可知最终执行的 protoc 命令是有 goctl rpc proto 和 proto 文件中 package 以及go_package 相关信息进行拼凑出来的。

使用 goctl 生成 grpc 代码的同学应该都会发现,goctl并没有把所有和 protoc 相关的命令都暴露出来,对于常规的 grpc 生成目前 --import_path 和 --go_opt 就基本够了,但这也是 goctl 生成 grpc 的弊端之一。

总结现有生成方案有一下缺陷:

  • protoc 原生flag 被 goctl 屏蔽,无法贴近protoc 丰富的用法
  • 用户安装的protoc-gen-xxx插件被限制,用户没办法自由选择自己需要的插件,在此之前我们一直推荐安装
$ go get -u github.com/golang/protobuf/protoc-gen-go@v1.3.2

其实该仓库已经不推荐使用了,官方现在推荐的 grpc 代码需要依赖两个插件,详情请移步至 grpc 快速开始

  • 由于问题 2,因此 goctl 在生成 zrpc 代码时会做对不同 protoc-gen-go(这里指的是新旧两个[golang、google]维护protoc-gen-go的插件)的兼容产生各种生成报错问题
  • 用户无法通过 goctl 很自如的生成使用了 import 的 proto 文件,命令不知道该怎么执行

改进方案

目标

对 protoc 透明,goctl 不再屏蔽 protoc细节,goctl 只充当父进程启动 protoc,对于 protoc 的所有指令用法沿用protoc和 grpc 官方的用法。

收益

  • 对 protoc 透明,所有关于protoc生成 grpc 遇到问题都会有章可循,查看 protoc 官方使用说明即可
  • 减少生成复杂度,goctl 只关注 zrpc 部署代码生成,即 client、server 实现代码
  • 变被动为主动,不再受protoc-gen-go插件版本、维护仓库带来的各种问题
  • 降低耦合,不用再通过 goctl 内部去帮用户识别需要的 protoc 参数去组装 protoc

方案

旧版兼容

平滑迁移指令,全新替换现有生成方案,原有通过如下命令生成 zrpc

$ goctl rpc proto ...

将会 log 一个不推荐使用的警告,并且告知代替方案,过渡 3-5 个 release 周期,输出示例:

$ goctl rpc proto -src user.proto -dir .

deprecated: it will be removed in the feature, zrpc code generation please use %q instead",
		"goclt rpc protoc
$ goctl rpc proto --help

deprecated: zrpc code generation use "goctl rpc protoc" instead, for the details see "goctl rpc protoc --help

指令改进

$ goctl rpc ${protoc_usage}

即通过 goctl rpc 后跟 protoc原生用法指令即可,如生成 user.proto的 grpc

$ goctl rpc protoc user.proto --go_out=plugins=grpc:. 

生成逻辑

  • Goctl 仅作为父进程启动 protoc 生成 grpc 代码,对 protoc 所需要的所有参数 goctl 均不参与
  • Goctl 作为 protoc 插件读取 protoc 序列化的中间信息并生成 client、server 代码,goctl 作为 potoc 插件与 protoc-gen-go互不关联,独立生成。

其他

此版本会废弃那些指令?

  • goctl rpc proto
  • goctl rpc new

此版本会限制用户安装指定版本的 protoc-gen-go插件吗?

不会,用户任意安装,与之对应的 grpc 生成指令在 goctl rpc 后指定 protoc 指令即可,完全贴近原生 protoc 使用

生成目录结构会有变化吗?

除 grpc 目录会变化外,其他均不会变化

为什么 grpc 目录较原版本会发生变化?

此版本将 protoc 生成 grpc 代码的控制权交由用户自行掌握,其实对 protoc 指令生成 grpc 代码时完全透明的,goctl 不再做参数拼接,插件版本检测、兼容,输出目录控制等,因此用户在执行protoc xx.proto --go_out=xxx时就是grpc 的输出目录

为什么 goctl 要包裹protoc?

为了识别 protoc 中的 proto 文件路径,然后生成 zrpc 代码,如grpc 代码的 server、client 实现是需要知道 proto 定义的内容的,但 grpc 代码生成和 zrpc 代码生成是隔离的,互不影响,如果 grpc 代码生成报错,那就需要去了解 protoc 指令了,除此外用户还需要了解一下 grpc 的文档。

生成grpc 和 zrpc代码有何区别?

grpc 代码是protoc工具配合 protoc-gen-go | protoc-gen-go-grpc 插件生成的代码,在 goctl 中是独立的部分,只是 goctl 作为父进程去启动protoc而已,这就是和原版本最大的区别,生成 zrpc 代码是指创建 go-zero 规范的文件目录,相关配置文件,main 函数,grpc 的 server 和 client 实现等。

源码pr(WIP)

see #1251

内测

goctl_protoc-beta.zip

This issue is stale because it has been open for 30 days with no activity.

This issue was closed because it has been inactive for 14 days since being marked as stale.

This issue is stale because it has been open for 30 days with no activity.

This issue was closed because it has been inactive for 14 days since being marked as stale.

不建议废弃rpc new命令,这个命令的场景还是很多的

Bot detected the issue body's language is not English, translate it automatically. 👯👭🏻🧑‍🤝‍🧑👫🧑🏿‍🤝‍🧑🏻👩🏾‍🤝‍👨🏿👬🏿


It is not recommended to abandon the rpc new command. There are still many scenarios for this command.