1. 原理
protoc 从proto文件中抠出来结构化的数据,然后以子进程的方式启动插件,调插件、把结构化数据传给插件,让插件做生成,然后 protoc 主进程把生成的内容写进文件里
https://www.hitzhangjie.pro/blog/2017-05-23-protoc%E5%8F%8A%E6%8F%92%E4%BB%B6%E5%B7%A5%E4%BD%9C%E5%8E%9F%E7%90%86%E5%88%86%E6%9E%90%E7%B2%BE%E5%8D%8E%E7%89%88
Q: 如何告诉 protoc 用什么插件
把插件编译好放进 path, 然后在运行 protoc 的时候带上命令行参数 –${NAME}_out
, protoc 就会尝试运行该插件
https://rotemtam.com/2021/03/22/creating-a-protoc-plugin-to-gen-go-code/
以 protoc-gen-go 插件为例:
https://www.hitzhangjie.pro/blog/2017-05-23-protoc%E5%8F%8A%E6%8F%92%E4%BB%B6%E5%B7%A5%E4%BD%9C%E5%8E%9F%E7%90%86%E5%88%86%E6%9E%90%E7%B2%BE%E5%8D%8E%E7%89%88/#2-protoc%E6%8F%92%E4%BB%B6%E5%B7%A5%E4%BD%9C%E5%8E%9F%E7%90%86%E5%88%86%E6%9E%90
生成代码的核心原理还是拼字符串。
比如拼 enum:
再比如根据 service 生成 gRPC相关代码:
image.png
Q: gRPC 生成时,用的什么模板、传了什么struct 给模板
A: 没用模板文件,直接用代码命令式的拼字符串
2. protoc 插件开发示例
- protoc-gen-go-gin
Go工程化(五) API 设计下: 基于 protobuf 自动生成 gin 代码 - Mohuishou (lailin.xyz)
https://github.com/mohuishou/protoc-gen-go-gin
从 protobuf 文件中生成使用 gin 的 http rpc 服务。是一个 protoc-gen-go 的子插件,类似于 gRPC 子插件。
原理是通过 embed 把模版嵌入,用 go 原生模板引擎渲染:
image.png
比如,先生成一个 interface, 作为供开发者实现的 ”component interface“, 或者叫 "handler interface":
image.png
不过 component interface 里用到的 struct 还是 pb.go 里的struct,没有和 pb 解耦
生成 server struct 和工厂函数:
image.png
让 server struct 实现 http api 里的每个 method,并且生成实现逻辑,即先检查字段,最后委托给 component interface
image.png
生成出来的 server struct 代码长这样:
image.png
image.png
检查字段的逻辑,其实是靠 proto 里的 option 声明,然后在代码里做计算、得到关键 flag,最后模板渲染时根据 flag 渲染字段检查逻辑:
image.png
- kratos/cmd/protoc-gen-go-http
kratos/cmd/protoc-gen-go-http at main · go-kratos/kratos (github.com)
3. 如何开发生成 go 代码的插件
最好基于一些开发框架来写插件,写起来更方便
3.1. 基于 protogen 开发
protogen 是官方提供的一个 go 开发框架
https://rotemtam.com/2021/03/22/creating-a-protoc-plugin-to-gen-go-code/
-
protoc-gen-markdown 开发教程
https://taoshu.in/go/create-protoc-plugin.html -
如何通过 protoc 命令行传参,把参数传给 plugin
image.png
https://rotemtam.com/2021/03/22/creating-a-protoc-plugin-to-gen-go-code/
-
如何 debug
没法debug,只能靠打印日志。开发完 go install 之后,运行 protoc。
可以打印到文件里,或者stderr里:
如果每次调试都需要编译一下会特别麻烦,也无法使用debug等工具
image.png
或者通过--descriptor_set_out
参数把整体结构打印到标准输出
protoc 工具插件和生态 - 白云辉 - 博客园 (cnblogs.com)
https://stackoverflow.com/questions/37730781/how-can-i-get-an-internal-representation-out-of-protoc
https://stackoverflow.com/questions/39171581/proto-descriptor-from-proto-schema-file-or-string
-
怎么把 message 转成 struct 结构
protogen 没提供获取 go struct 的接口。protoc-gen-go 是自己写的 if else
image.png
准备自己写个 helper 库
3.2. 基于 protoc-gen-gotemplate
- 缺少文档,你不知道字段有哪些
- 没法像js 一样,用代码做model处理,必须用模板引擎语法做处理
Idea:
- 允许代码做model处理,比如每个 template 有个同名的 go文件,类似 html 有对应的 js
- 允许用html和前端框架做渲染
网友评论