这是我参与「第五届青训营 」伴学笔记创作活动的第 7 天
Hertz[həːts] 是字节跳动开源的 Golang 微服务 HTTP 框架,底层的网络库使用了字节跳动开发的高性能 NIO(Non-blocking I/O)网络库Netpoll,目前也支持切换回官方的网络库。
注:Netpoll目前(2023.1.24)无法在Windows环境下使用,所以Windows用户只能使用官方的网络库。
作为国内的开源框架,文档还是比较友好的。
官方文档:Hertz | CloudWeGo
另外 Hertz 开源于2022年6月(Monday, June 20, 2022),第三届字节青训营貌似并没有相关的课程 。
架构设计
这个图如果只是入门的话,看看就行。这个图的结构是指Hertz本身的架构设计,不是用Hertz开发的项目的架构。
不过倒是可以从图中看出Hertz支持的一些协议(HTTP/3,Websocket等)以及特点。
框架特点
高易用性、高性能、高扩展性、多协议支持、网络层切换的能力等
另外不同于一些公司专门有部门来做开源,字节做的Go框架似乎是先满足字节内部的客制化需求(即公司自己内部的定制化需求),积累到一定体量之后开源出来的。这样也蛮好的,起码代码经过实战检验。
快速开始
准备环境
安装好 Go 语言开发环境即可,最好是最新的稳定版(2023.1.24最新稳定版是 1.19.5)
再次提醒:Hertz 支持 Linux、macOS、Windows 系统,但是 NetPoll 不支持 Windows 系统。
代码生成工具 hz
先确保 GOPATH 环境变量已经被正确地定义(例如 export GOPATH=~/go)并且将$GOPATH/bin添加到 PATH 环境变量之中(例如 export PATH=$GOPATH/bin:$PATH);请勿将 GOPATH 设置为当前用户没有读写权限的目录
Windows下添加环境变量即可,Linux和Mac貌似需要在命令行执行上面的两行命令。
命令行输入命令来安装 hz:go install github.com/cloudwego/hertz/cmd/hz@latest
输入 hz -v 要是有版本信息就是成功了
编写 IDL
这里以 Thrift 作为 IDL。
Thrift IDL 语法可参考官方文档:Thrift interface description language。
初学者直接看官方文档可能有点抽象,可以看下这篇文章:Thrift & IDL 介绍
新建一个包 idl,在 idl 里新建一个文件 calc.thrift,编写 IDL。
namespace go calc
struct AddReq {
1: i64 Num1; // 什么都不写,默认绑定所有位置的参数
2: i64 Num2;
}
struct AddResp {
1: i64 Sum;
}
service CalcService {
AddResp AddMethod(1: AddReq request) (api.post="/add");
}
生成代码
生成代码并拉取依赖
hz new -module add -idl idl/calc.thrift
go mod tidy
后续如果有新的接口或者其他更新内容,可以根据 IDL 更新项目。
hz update -idl idl/calc.thrift
生成完代码之后可以看到有一个项目根目录下有个 .hz 的文件。
// Code generated by hz. DO NOT EDIT.
hz version: v0.5.2
内容其实没啥,但是使用 hz 更新代码的时候,会去找这个文件。这个文件就相当于是项目生成代码的中心,运行 hz 命令时,要保证命令行所在目录和 .hz 文件所在目录一致。
这个时候会发现,项目的代码都是直接生成在根目录下的,那要是用了微服务架构,想把项目写在 cmd 目录下怎么办呢?
使用 hz new -help 查看相关的命令行参数
NAME:
hz new - Generate a new Hertz project
USAGE:
hz new [command options] [arguments...]
OPTIONS:
--idl value [ --idl value ] Specify the IDL file path. (.thrift or .proto)
--service value Specify the service name.
--module value, --mod value Specify the Go module name.
--out_dir value Specify the project path.
--handler_dir value Specify the handler path.
--model_dir value Specify the model path.
--client_dir value Specify the client path. If not specified, IDL generated path is used for 'client' command; no client code is generated for 'new' command
部分参数这里就直接省略了,主要关注后缀为 _dir 的命令,这些是指定项目生成路径的命令。
- 比如 out_dir: 指定项目生成路径
这里演示下使用命令行参数指定代码生成路径
hz new -module calc -idl idl/calc.thrift -out_dir cmd/api
对 IDL 进行修改,添加 Sub 接口
namespace go calc
struct AddReq {
1: i64 Num1; // 什么都不写,默认绑定所有位置的参数
2: i64 Num2;
}
struct AddResp {
1: i64 Sum;
}
struct SubReq {
1: i64 Num1; // 什么都不写,默认绑定所有位置的参数
2: i64 Num2;
}
struct SubResp {
1: i64 Res;
}
service CalcService {
AddResp AddMethod(1: AddReq request) (api.post="/add");
SubResp SubMethod(1: SubReq request) (api.post="/sub");
}
更新
cd cmd/api
hz update -idl ../../idl/calc.thrift # ../../ 表示当前目录往上两级的目录,这里指项目根目录
最后生成的最终结果如下图
补充逻辑并运行
简单添加下 add 的逻辑
func AddMethod(ctx context.Context, c *app.RequestContext) {
var err error
var req calc.AddReq
err = c.BindAndValidate(&req)
if err != nil {
c.String(consts.StatusBadRequest, err.Error())
return
}
resp := new(calc.AddResp)
resp.Sum = req.Num1 + req.Num2
c.JSON(consts.StatusOK, resp)
}
在 cmd/api 目录下运行项目
go run .
使用 Apifox 获取其他方式发送请求
结果如下