hz:Hertz 框架官方 IDL 代码生成器完整使用指南
【免费下载链接】hertzGo HTTP framework with high-performance and strong-extensibility for building micro-services.项目地址: https://gitcode.com/GitHub_Trending/he/hertz
hz是 Hertz 目录。它负责解析 Thrift / Protobuf 两种 IDL(Interface Definition Language)文件,并一键生成完整可运行的 Hertz 项目脚手架,包括 handler、路由、model 数据模型与 client 客户端代码。阅读本文后,你将掌握hz new / update / model / client四大命令的完整用法、全部核心参数含义、自定义模板机制,以及 CLI 与插件双模式运行的底层原理。
快速上手
在仓库的cmd/hz目录下构建二进制,然后即可基于 IDL 生成项目:
# 构建 cd cmd/hz && go build -o hz . # 从 Thrift IDL 生成新项目 hz new --idl api.thrift --module github.com/example/myservice # 从 Protobuf IDL 生成新项目 hz new --idl api.proto --module github.com/example/myservice # IDL 变更后增量更新已有项目(模块名自动从 go.mod 读取) hz update --idl api.thrift # 仅生成 model 代码(模块名自动从 go.mod 读取) hz model --idl api.thrift # 生成 client 代码(模块名自动从 go.mod 读取) hz client --idl api.thrift --base_domain localhost:8888--module(别名--mod)用于指定 Go module 名称。若当前目录或其上层目录已存在go.mod,hz 会通过util.SearchGoMod自动探测模块名,无需再手动指定;若未指定且找不到go.mod,则会以 GOPATH 相对路径作为模块名并自动生成go.mod(对应源码逻辑见 config/argument.go 的checkPackage实现)。
四大命令
| 命令 | 说明 |
|---|---|
new | 从 IDL 搭建全新 Hertz 项目,生成工程布局、handler、路由与 model |
update | 为 IDL 中新增的方法增量添加 handler/路由,不覆盖已有代码 |
model | 仅根据 IDL 类型生成 Go 结构体定义(models) |
client | 根据 IDL service 定义生成 Hertz HTTP 客户端代码 |
命令入口定义在 app/app.go 的Init()中,每个命令都通过urfave/cli注册并绑定各自支持的 flag 集合;main.go首先调用app.PluginMode()检查插件模式(见下文架构部分),随后进入正常 CLI 流程(main.go)。
参数(Flags)全解
全局参数
| 参数 | 说明 |
|---|---|
--verbose,-vv | 开启 verbose(debug 级别)日志 |
--verbose会调用logs.SetLevel(logs.LevelDebug)输出详细调试信息;不开时默认只输出 warning 级别以上日志(app/app.go)。
项目结构相关参数
| 参数 | 适用命令 | 说明 |
|---|---|---|
--idl | 全部 | IDL 文件路径(.thrift或.proto),可多次指定 |
--module,--mod | 全部 | Go module 名称(若存在go.mod则自动探测) |
--service | new | 服务名(默认:hertz_service) |
--out_dir | new,update,model | 项目输出目录(默认:当前目录) |
--handler_dir | new,update | handler 目录,相对out_dir(默认:biz/handler) |
--model_dir | 全部 | model 目录,相对out_dir(默认:biz/model) |
--router_dir | new | router 目录,相对out_dir(默认:biz/router) |
--client_dir | new,update,client | client 输出目录。对new/update:不指定则不生成 client 代码;对client:默认使用由 IDL 推导的路径 |
--force_client_dir | client | client 输出目录,不带 IDL 命名空间子目录 |
--use | new,update,client | 从外部包导入 model,而非本地生成 |
默认目录常量biz/model、biz/router、biz/handler定义于 meta/const.go。注意:handler_dir、model_dir、router_dir等必须为相对out_dir的相对路径,绝对路径会直接报错(config/argument.go 的checkPath)。--use的场景通常是 model 由独立的公共仓库统一维护,此时 hz 会跳过本地的 model 生成(Thrift 场景下还会输出提示信息'model code' is not generated due to the '-use' option,见 meta/const.go)。
代码生成参数
| 参数 | 适用命令 | 说明 |
|---|---|---|
--handler_by_method | new,update | 每个方法生成独立 handler 文件(默认:每个 service 一个文件) |
--sort_router | new,update | 对路由注册代码排序,保证输出确定性 |
--no_recurse | 全部 | 只生成主 IDL 的 model,跳过 include/import 的依赖 IDL |
--force,-f | new | 强制覆盖已有项目 |
--force_client | client | 即使hertz_client.go已存在也强制重新生成 |
--base_domain | client | 生成的 client 代码中默认请求域名 |
--enable_extends | new,update,client | 解析 Thrift IDL 中的extends关键字 |
--handler_by_method对应两种 handler 生成模式:默认的"按 service"模式将所有 handler 汇聚在一个文件(如user_service.go),update时通过handler_single.go模板追加新方法;"按方法"模式则为每个方法生成独立文件(如create_user.go),update时新建文件但绝不修改已有文件(详见 DESIGN.md 的 Handler Generation 小节)。
Struct Tag 参数
| 参数 | 适用命令 | 说明 |
|---|---|---|
--snake_tag | 全部 | form、query、jsontag 使用 snake_case 命名 |
--json_enumstr | 全部 | JSON 枚举字段使用字符串值而非数字(仅 Thrift) |
--unset_omitempty | 全部 | 移除生成结构体 tag 中的omitempty |
--pb_camel_json_tag | 全部 | JSON tag 使用 camelCase(仅 Protobuf) |
--rm_tag | 全部 | 移除默认 tag(如--rm_tag json)。显式注解的 tag 会被保留 |
--query_enumint | client | client 代码中枚举 query 参数使用数值 |
--enable_optional | client | Thrift 可选字段未设置时不放入 query 参数 |
这些 tag 选项最终通过GetThriftgoOptions拼接到 thriftgo 的go:生成选项字符串中——默认选项为reserve_comments,gen_json_tag=false,--json_enumstr会追加json_enum_as_text,并总是追加package_prefix=以保证生成 import 路径与输出结构一致(config/cmd.go)。
IDL 编译器透传参数
| 参数 | 适用命令 | 说明 |
|---|---|---|
--proto_path,-I | 全部 | 添加 Protobuf import 的搜索路径 |
--thriftgo,-t | 全部 | 透传给 thriftgo 的参数(如-t naming_style=golint) |
--protoc,-p | 全部 | 透传给 protoc 的参数 |
--thrift-plugins | new,update,client | 额外的 thriftgo 插件({name}:{options}) |
--protoc-plugins | new,update,client | 额外的 protoc 插件({name}:{options}:{out_dir}) |
--option_package,-P | new,update | 将 IDL include 路径映射为 Go import 路径({include}={import}) |
--trim_gopackage,--trim_pkg | 全部 | 裁剪 protobufgo_package前缀,避免生成过深的嵌套目录 |
以 Protobuf 场景为例,BuildPluginCmd会为 protoc 构造--plugin=protoc-gen-hertz=<hz二进制路径>、--hertz_out=<输出目录>与--hertz_opt=<序列化参数>三个关键参数(config/cmd.go);protoc 插件必须以plugin_name:options:out_dir三段式传入,否则会直接报错退出。protoc 的 well-known types 需要额外的-Iinclude 路径,这点在generate.sh中体现得很清楚(自动探测 protoc 同级 include 目录或/usr/include、/usr/local/include等系统路径)。
模板定制参数
| 参数 | 适用命令 | 说明 |
|---|---|---|
--customize_layout | new | 自定义工程布局模板 YAML 路径 |
--customize_layout_data_path | new | 渲染布局模板用的 JSON 数据文件路径 |
--customize_package | new,update,client | 自定义 package 模板 YAML(覆盖 handler/router/middleware 模板) |
--exclude_file,-E | 全部 | 排除某文件路径,不生成/不更新 |
--customize_layout指定布局配置后,GenerateLayout会读取该 YAML 替代默认布局(app/app.go);若同时提供了--customize_layout_data_path的 JSON 数据文件,则完全由数据文件驱动渲染(GenerateByConfig,见 generator/layout.go),否则仍然按 service 信息渲染。
自定义模板(Custom Templates)
hz 的全部生成代码都由 Go template 渲染。创建一个 YAML 配置文件,并用--customize_package传入:
layouts: - path: biz/handler/handler.go # 覆盖默认 handler 模板 delims: ["{{", "}}"] body: | package {{.PackageName}} // your custom handler template... - path: biz/custom/{{.ServiceName}}.go # 新建文件,path 支持模板变量 delims: ["{{", "}}"] loop_service: true # 每个 service 生成一个文件 update_behavior: type: append # update 时的行为:skip/cover/append append_key: method # 按 method 或 service 追加 append_content_tpl: | // new method: {{.Name}} body: | package custom // your template...模板的 update 行为有三种:
skip—— 不修改已存在的文件;cover—— 完全覆盖已有文件;append—— 向已有文件追加新内容(例如新增的 handler 方法)。
模板系统还支持自定义定界符(当你的模板本身包含 Go template 语法、需要避开{{ }}冲突时非常有用)、按 service 循环(loop_service)或按 method 循环(loop_method)生成多份文件等能力(DESIGN.md)。
示例输出:用 generate.sh 验证生成结果
仓库提供了 generate.sh,使用cmd/hz/testdata下的测试 IDL 文件(Thrift 与 Protobuf2/3 两套 psm 用例)完整跑一遍new → update → model → client全流程:
cd cmd/hz # 为所有 IDL 类型生成示例(输出到 generate_out/) ./generate.sh # 只生成指定目标 ./generate.sh thrift proto3 # CI 模式:生成 → 校验产物可编译 → 清理 ./generate.sh --verify --clean可选目标:thrift、proto2、proto3、handler_by_method(不传则默认全部)。脚本细节:
--verify会对每个目标执行go mod tidy && go build .,确保生成的代码真实可编译;--clean在验证通过后删除输出目录,适合 CI 流水线;--hz PATH可指定 hz 二进制路径,默认从源码go build -o hz .构建;- 每个目标在子目录中依次执行
hz new(带-f强制覆盖)、hz update、hz model、hz client --client_dir=hertz_client,完整覆盖四个命令的可运行性; - Protobuf 目标会尝试定位 protoc 的 well-known types include 目录并作为
-I传入(generate.sh)。
对应的测试 IDL 文件位于 cmd/hz/testdata/thrift/psm.thrift、cmd/hz/testdata/protobuf3/psm/psm.proto 等路径,可作为编写自定义 IDL 的参考样本。
依赖的 IDL 编译器
- thriftgo—— Thrift 编译器。若系统中不存在,hz 会自动安装最新版本;若已存在但版本低于 v0.2.0,也会自动更新到最新版(逻辑见 config/cmd.go 的
lookupTool); - protoc—— Protobuf 编译器,必须手动安装,hz 不会自动安装(若缺失会直接提示 "please install it first")。
lookupTool的查找顺序为:PATH中的thriftgo/protoc→$GOPATH/bin下的同名工具。Thrift 场景下若go.mod中缺少replace github.com/apache/thrift => github.com/apache/thrift v0.13.0,hz 还会在new结束时给出对应警告提示(meta/const.go)。
架构:CLI 与插件双模式执行
hz 内部采用"CLI 模式 + 插件模式"双模式设计,完整设计文档见 cmd/hz/DESIGN.md。
双模式执行模型
- CLI 模式(正常模式):解析命令行参数、生成工程布局,然后以子进程方式调用 IDL 编译器(thriftgo/protoc);
- 插件模式:当被 IDL 编译器以插件方式回调(
thrift-gen-hertz/protoc-gen-hertz)时,从 stdin 读取解析好的 AST,生成 Hertz 专属代码。
因此一条hz new命令实际上会运行两次 hz:
User -> hz (CLI mode) -> thriftgo/protoc -> hz (Plugin mode) -> generated code插件模式通过环境变量HERTZ_PLUGIN_MODE检测——CLI 在调用编译器前会设置该变量(config/cmd.go),main()开头的app.PluginMode()则检查该变量并决定是否以插件身份执行(app/app.go、main.go)。
代码生成流水线
- 布局生成(仅
new命令):创建工程骨架——含 Hertz server 启动代码的main.go、含依赖声明的go.mod、路由注册入口router.go,以及biz/handler/、biz/model/、biz/router/目录结构。默认main.go模板内容为server.Default()+register(h)+h.Spin(),可直接在 generator/layout_tpl.go 中查看;完整默认布局(含biz/dal、biz/service、script、conf等目录)也在该文件中定义; - 插件执行:IDL 编译器解析
.thrift/.proto文件后,把 AST 传给插件模式下的 hz。插件先转换AST 为内部的Service与HttpMethod结构体,提取 HTTP 注解(路径、方法、序列化方式),再解析跨 IDL 文件的类型引用为 Go import 路径,最后通过HttpPackageGenerator生成代码; - Handler 生成:见上文
--handler_by_method说明; - 路由生成:路由以树结构(
RouterNode)组织,通过插入每个方法的 HTTP path 构建;随后遍历树为中间件分组分配唯一名称(DyeGroupName),渲染为r.Group()/r.GET()等 Go 路由注册代码,并为每个路由组生成中间件 stub; - 模板系统:支持自定义定界符、三种 update 行为、按 service/method 循环生成,以及通过
--customize_package覆盖任意默认模板。
.hz清单文件与参数传递
项目根目录的.hzYAML 文件(由hz new生成)记录了三类信息:
- 生成该项目所用的 hz 版本;
- handler、model、router 目录路径。
这使hz update无需用户重复指定全部参数即可定位已有生成代码——update命令会先通过InitAndValidate加载并校验.hz(缺失或版本非法都会报错),再用其中的目录信息补全参数(app/app.go、meta/manifest.go)。.hz文件内容大致形如:
// Code generated by hz. DO NOT EDIT. hz version: v0.9.7 handlerDir: biz/handler modelDir: biz/model routerDir: biz/router由于插件是 IDL 编译器的子进程而非 hz 的直接子进程,CLI 与插件之间的参数传递通过反射序列化完成:util.PackArgs将参数打成逗号分隔字符串,经编译器插件选项透传,插件再用util.UnpackArgs反序列化还原(DESIGN.md)。格式形如FieldName=value,SliceField=val1;val2;val3,MapField=k1=v1;k2=v2。
小结
hz 为 Hertz 项目提供了一条"IDL 即代码"的完整链路:new搭建骨架、update增量演进、model单独产出数据层、client一键生成调用方。通过丰富的 tag 风格选项、编译器参数透传、可深度定制的模板系统,以及.hz清单驱动的增量更新机制,它能够稳定适配从个人微服务到大规模团队协作的代码生成需求。若需深入源码,建议从 app/app.go(命令编排)、generator(生成引擎)、thrift 与 protobuf(两套 IDL 插件)三条主线入手阅读。
【免费下载链接】hertzGo HTTP framework with high-performance and strong-extensibility for building micro-services.项目地址: https://gitcode.com/GitHub_Trending/he/hertz
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考