CloudWeGo Study Group 是由 CloudWeGo 社区发起的学习小组,开展以 30 天为一期的源码解读和学习活动,帮助新成员融入社区圈子,和社区 Committer 互动交流,并学习上手 CloudWeGo 几大框架项目。目前 CSG 第二期——Hertz 框架篇已经正式启动!
本期活动期间将会安排 4 期直播分享,主题分别为:
- 从精通烤肉到精通HTTP——HTTP 框架初识;
- 如何利用命令行工具 Hz 快速开发 Hertz 服务;
- 如何对开源项目进行学习——从全局到局部分析的思路;
- 代码学习无止境,程序员的未来归于何处。
本文为 CSG 第二期第二场直播中字节跳动架构研发工程师范广宇分享内容。
01 嘉宾介绍
本期分享的内容主要分为以下四个部分:
- 环境配置 & 工具介绍;
- IDL 的介绍 & Hz 拓展的 IDL 注解;
- Hz 运行模式;4. Demo 实践:利用 Hz 创建 HTTP 服务(一个串联 Hertz 和 Kitex 的 Demo)。
02 环境配置 & 工具介绍
Go 环境配置
首先进行 Go 环境的配置。根据使用系统的不同,在 Go 语言官网中下载对应的安装包。
安装包链接:https://go.dev/doc/install
安装完成后,需要配置两个环境变量:
- 定义 GOPATH 环境变量:export GOPATH=~/go;
- 将 GOBIN 添加到 PATH 环境变量:export PATH=$GOPATH/bin:$PATH。
工具介绍
- Hz:Hertz 命令行工具,基于 IDL 生成 Hertz 代码脚手架;
- Kitex:Kitex 命令行工具,基于 IDL 生成 Kitex 代码脚手架;
- Thriftgo:基于 Go 语言的 Thrift 编译器,可以将定义的 Thrift IDL 编译生成相应的 Go 代码。此外它还提供插件模式,可以拓展生成的代码;
- Protoc:Protobuf 的官方编译器。支持多种语言代码生成,同样提供插件模式拓展生成的代码。
目前 Hz 的开发是基于 Thriftgo 和 Protoc 的插件模式进行拓展代码的生成,如果大家需要安装这些工具,欢迎到 CloudWeGo 官网找到对应的下载链接进行安装。
03 IDL 的介绍
IDL 的定义
IDL 的全称是 Interface Definition language,即接口定义语言。为对接口进行通用性的描述,其一般都包含以下两个部分:
1. 通用的数据类型及关键字。IDL 与目标语言的类型存在映射关系,例如 i32 对应到 Go 语言就是 Int 类型,关键字 Struct 会对应为 Go 语言中的结构体。
2. 通用的接口描述能力。一般 IDL 会提供一个类似函数调用的形式来描述接口,并使用通用的数据类型组合给出请求参数和返回参数的类型。
例如,如下图所示定义一个接口,通过通用的数据类型定义请求体 Req 和返回体 Resp,拼接出一个名为 Method1
的接口,它接收的是一个 Req 类型的请求参数,并向调用者返回一个 Resp 类型的参数。
由于 IDL 是一种通用性的描述语言,它独立于特定的语言及环境,因此它为跨机器通信提供了便利,相当于提供了一个供服务者和调用者共同遵守的协议。
目前大多数 RPC 框架都需要用户定义 IDL ,并提供代码生成以及序列化和反序列化的功能。那么,既然 IDL 是为 RPC 框架服务的,为什么 HTTP 框架 Hertz 也需要使用 IDL 呢?
事实上,Hertz 并不是强依赖于 IDL 的,我们只是利用了 IDL 提供的接口描述能力以及拓展注解的能力,进而为用户生成一个可以快速开发服务的代码脚手架。
Hz 拓展的注解
Thrift Field 注解
Field 注解指在 IDL 定义的 Struct 每个域上添加的、主要用来和 Hertz 参数绑定及校验进行配合的注解。下面举几个简单的注解例子:
api.query
注解会在生成的 Go 代码中添加一个query
Tag,当用户使用 Hertz 的参数绑定功能后,会根据 Tag 的内容为结构体赋值。例如query
注解中,如果用户请求的query
参数为query=hertz
,那么使用了 Hertz 的绑定能力后,对应的成员变量也将会被赋值为hertz
。Gotag
会将其定义的所有内容透传给 Go Struct,并且允许用户自定义生成 Tag 以满足其需求。例如上图 Field 7 中就会生成一个名为json
的Tag 和一个名为goTag
的Tag。VdTag
用来进行参数校验。目前 Hertz 的参数校验能力就是利用了 Vd 注解。
Vd 参数校验注解
一般 Vd 也是作为一个注解存在的,它会在生成的 Go 代码中以 Go Tag 的形式存在。
如图所示,以 Name
的成员变量为例:
首先,Name
成员变量的 VdTag
声明了很多信息,这些内容也是相关的校验规则。
$
表示该变量本身,图中表示该变量的值不能为 Alice;&&
表示"与"操作,后面的表达式表示该变量的长度必须小于 100;msg
关键字,表示验证失败返回的报错信息。例如图中如果用户的Name
为Alice
,就会返回Name can not be Alice
的报错信息。
除了内置的多种验证函数之外,Hertz 的参数校验能力还支持自定义校验函数。如果用户有一些特别的、复杂的校验逻辑,就可以写在一个校验函数中,最后透传到我们的 Vd 注解里。此外,Hertz 的参数校验和绑定能力还支持多种用法。
更多使用方法请参考:https://www.cloudwego.io/zh/d...
Thrift Method 注解
method
注解是指我们在定义接口时描述 HTTP 方法的注解。如图,每一个注解都对应了一个 HTTP 的请求方法。
例如 Method1
接口,如果请求 Method1
,就需要在客户端或者请求端发送一个 HTTP 的 Get 请求。在生成代码之后 Hz 工具会读取请求的路由地址,并为这些接口生成对应的路由注册以及路由组的注册,此外还会生成一个默认的处理器函数。
下图展示了用 IDL 给上图中定义的接口生成的代码。以红框为例,给出了一个 userid
的路由组,我们在路由组里面可以注册一些中间件函数,当用户路由与中间件函数的前缀相匹配时即可执行。之后 Hz 工具会将所有的路由进行最终的注册,例如这里注册的 DELETE、GET、PUT、POST等方法,此外 Hz 还会为其生成一个默认的 Handler 逻辑。
Protobuf Field 注解
Protobuf 进行的注解拓展与 Thrift 是一样的,只是在使用的写法上略有不同。
在使用 Protobuf 写 IDL 的时候,需要提前引用 Hz 文档中提供的 api.proto
注解拓展文件,才能够正确地使用注解并生成想要的代码。
基于 Protobuf IDL 创建项目:https://www.cloudwego.io/zh/d...
Protobuf Method 注解
Protobuf 的 Method 注解和 Thrift 也是一致的,这里不再赘述,具体可以到 Hz 的使用手册中查询。
Hz 命令行工具使用:https://www.cloudwego.io/zh/d...
04 Hz 运行模式
Hz 在生成代码的时候一共需要三个工具:Hz、Thriftgo 和 Thriftgo 插件 thrift-gen-hertz。
目前我们的命令行工具 Hz 有两种运行方式:其一,Hz 工具本身作为一个命令行工具存在;其二,它也会作为一个 Thriftgo 的插件来运行。
Hz 运行的整个流程如下:
- 当用户使用了 Hz 命令后,Hz 工具本身会被系统调用,生成一些 Layout 相关代码,比如 Main 函数、Gomod 等文件。同时,Hz 会拼接 Thriftgo 命令调用 Thriftgo 来生成与 IDL 定义相关的代码;
- Thriftgo 会解析 IDL 并生成对应的语法树,然后将语法树以及透传给插件的选项一起写入到标准输入中;
- 当插件得到了这些语法树以及选项之后,插件会先在标准输入中读取到 Thriftgo 传过来的内容,然后解析语法树,并根据用户在 IDL 中拓展的相关 HTTP 注解来生成目标文件的内容;
- 当 thrift-gen-hertz 插件执行完毕后,会通过标准输出的形式将结果返回,Thriftgo 会从中读入插件的执行结果,并生成目标文件;
- 将整个流程执行的结果返回给我们的命令行 Hz 工具,命令行工具会将最终的结果呈现给用户。
05 Demo 实践
利用 Hz 创建 HTTP 服务
本节 Demo 会将 CloudWeGo 项目下的 Hertz 与 Kitex 串联在一起,为大家的实践提供参考。
如链路图所示,在这个链路中,首先由客户端发起一个 HTTP 请求访问 Hertz-Server,Hertz-Server 在这里充当了一个 API 层的服务,它可以将用户的请求经过一些简单处理,然后再交由给 Kitex-Server,最终所有的处理结果组合后,通过 HTTP 响应返回给客户端。
1. 链路执行的流程大致如下:
- 客户端发送 HTTP 请求给 Hertz-Server;
- Hertz-Server 把该请求打到对应路由的处理器函数中,完成一些业务逻辑的处理;
- 利用 Kitex-Client 将处理的结果发送 RPC 请求至 Kitex-Server;
- 通过 Kitex-Server 的处理,Kitex-client 会得到一个 RPC 响应的结果;
- 此时,Hertz 的 Handler 可以对执行结果采取进一步的处理;
- 最终,将所有的业务逻辑都通过一个 HTTP 响应的方式返回给客户端。
- 本节演示 Demo 需要实现的内容如下:
- 编写 RPC IDL 和 HTTP IDL。RPC IDL 用于生成 Kitex 代码,HTTP IDL 用来生成 Hertz 的脚手架代码;
- 生成 Kitex 代码,并完成 Kitex Server 端的逻辑编写;
- 使用 Hz 工具生成 Hertz server 侧的代码,并完成 Hertz Handler 逻辑的编写;
- 简单地发一个请求调试接口,判断逻辑是否已经调通。
此处还介绍了 Hz 的 update
命令:在 HTTP 的 IDL 中新增一个接口,使用 Hz update
命令,就可以更新 Hertz 侧的代码。代码更新完成后,编写新增的 Handler 逻辑,再进行接口的调试就完成所有的 Demo 内容了。
Demo 实践演示视频链接:
https://meetings.feishu.cn/s/...(第 23 分钟开始演示)
在演示中主要介绍了代码内容以及 Hz 生成脚手架的内容。
以上 Demo 不讲解 Kitex 代码,如果对 Kitex 代码有兴趣,演示 Kitex Demo 的所有代码在下方链接中,如有需要可以扩容到本地进行实践。
Kitex Demo:
https://github.com/cloudwego/...
- 在完成本次 Demo 实践后,基于脚手架开发的重点总结如下:
- 需要在
main.go
中完成业务配置的初始化工作; - 可以为特定的路由组添加中间件函数,以实现不同的路由组拥有不同的执行逻辑;
- 编写业务的 Handler 函数,实现具体的业务逻辑。
项目地址
GitHub:https://github.com/cloudwego
官网:www.cloudwego.io
【CSG 第二期】CloudWeGo 源码解读活动 ——“Hertz 框架篇”开始啦!
活动链接:https://mp.weixin.qq.com/s/GU...