protoc 插件开发

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 就会尝试运行该插件

image.png

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:

image.png

再比如根据 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
    https://rotemtam.com/2021/03/22/creating-a-protoc-plugin-to-gen-go-code/

    image.png

  • 如何 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

moul/protoc-gen-gotemplate: generic protocol generator based on golang's text/template (grpc/protobuf) (github.com)
看着不错,但:

  • 缺少文档,你不知道字段有哪些
  • 没法像js 一样,用代码做model处理,必须用模板引擎语法做处理

Idea:

  • 允许代码做model处理,比如每个 template 有个同名的 go文件,类似 html 有对应的 js
  • 允许用html和前端框架做渲染

你可能感兴趣的:(protoc 插件开发)