这是我参与「第五届青训营 」笔记创作活动的第 6 天 。
一、本堂课重点内容
本堂课主要讲了Hertz的基本用法;Hertz是Go的http框架之一 。 官方文档:Hertz | CloudWeGo
二、详细知识点
1.启动服务端监听
- Default和New的区别:Default会默认集成Recover中间件,而New不会使用任何默认配置
h := server.Default(server.WithHostPorts("127.0.0.1:8080"))
- Hertz默认集成了 Netpoll和Golang原生网络库 两个网络库,用户可以根据自己的场景选择合适的网络库以达到最佳性能 。
- 对于 Server 来说,默认使用 netpoll ,可以通过配置项进行更改:
server.New(server.WithTransport(standard.NewTransporter))
server.New(server.WithTransport(netpoll.NewTransporter))
- 对于 Client 来说,可以通过配置项进行更改:
client.NewClient(client.WithDialer(standard.NewDialer()))
client.NewClient(client.WithDialer(netpoll.NewDialer()))
- 配置完路由及其他设置后
h.Spin()开始监听
网络库选择
- 如果有启动 TLS Server 的需求,请使用 go net 网络库。netpoll 正在实现对 TLS 的支持。
- 由于网络库触发模式的不同:go net 为 ET 模型,netpoll 为 LT 模型,使得两个网络库的适用场景有一些不同。 在 ET 模型下,由框架处理 Read / Write 事件;在 LT 模型下,由网络库处理 Read / Write 事件。 使得在小包场景下,由于更优的调度策略使得 LT 性能更好;在大包场景下,由于读 / 写不受框架层控制,使得大量数据被读入内存而不能及时处理,可能会造成内存压力。
- 在较大 request size 下( request size > 1M ),推荐使用 go net 网络库加流式。
- 在其他场景下,推荐使用 netpoll 网络库,会获得极致的性能。
2.路由
1)注册方法
Hertz 提供了 GET、POST、PUT、DELETE、Any、PATCH、HEAD、OPTIONS、Handle、StaticFile/Static/StaticFS 方法用于注册路由。
- Hertz.Handle 这个方法支持用户手动传入 HTTP Method 用来注册方法,当用于注册普通的 HTTP Method 方法时和上述的方法作用是一致的,并且这个方法同时也支持用于注册自定义 HTTP Method 方法
- Hertz.Any 用于注册所有 HTTP Method 方法
- Hertz.StaticFile/Static/StaticFS 用于注册静态文件
- 代码示例:
h.GET("/get", func(ctx context.Context, c *app.RequestContext) {
c.String(consts.StatusOK, "get")
})
- 与gin框架不同的是Hertz使用了两个上下文 。
2)路由组
- Hertz 提供了路由组(
Group)的能力,用于支持路由分组的功能,同时中间件也可以注册到路由组上。 - 代码示例:
v1 := h.Group("/v1")
v1.GET("/get", func(ctx context.Context, c *app.RequestContext) {
c.String(consts.StatusOK, "get")
})
3)路由类型
- Hertz 支持丰富的路由类型用于实现复杂的功能,包括静态路由、参数路由、通配路由。
- 路由的优先级:
静态路由>命名路由>通配路由
①静态路由
形式为/Hertz,上文已经展示
②参数路由
- 形式为
/hertz/:version - 参数路由会匹配version处的一个参数,即可以匹配
/hertz/version,但不会匹配/hertz/ v := c.Param("version")取得参数
③通配路由
- 形式为
/hertz/:version/*action - 通配路由会匹配多个参数,即可以匹配
/hertz/v1/或hertz/v2/send a := c.Param("action")取得参数
3.Hertz参数绑定和校验
- 使用
BindAndValidate()、Bind()、Validate()进行绑定校验 - 参数绑定需要配合特定的go tag使用
r.GET("/hello", func(c context.Context, ctx *app.RequestContext) {
type Test struct {
A string `query:"a" vd:"$!='Hertz'"`
}
// BindAndValidate
var req Test
err := ctx.BindAndValidate(&req)
// Bind
req = Test{}
err = ctx.Bind(&req)
// Validate,需要使用 "vd" tag
err = ctx.Validate(&req)
})
上例中会将query参数绑定到a变量中,校验成功的条件为a != "Hertz"
支持的tag以及参数绑定优先级
| path | 绑定 url 上的路径参数,相当于 hertz 路由{:param}或{*param}中拿到的参数。例如:如果定义的路由为: /v:version/example,可以把 path 的参数指定为路由参数:path:"version",此时,urlhttp://127.0.0.1:8888/v1/example,可以绑定path参数"1" |
|---|---|
| form | 绑定请求的 body 内容。content-type -> multipart/form-data 或 application/x-www-form-urlencoded,绑定 form 的 key-value |
| query | 绑定请求的 query 参数 |
| header | 绑定请求的 header 参数 |
| json | 绑定请求的 body 内容 content-type -> application/json,绑定 json 参数 |
| raw_body | 绑定请求的原始 body(bytes),绑定的字段名不指定,也能绑定参数。(注:raw_body 绑定优先级最低,当指定多个 tag 时,一旦其他 tag 成功绑定参数,则不会绑定 body 内容。) |
| vd | 参数校验,校验语法 |
4. Hertz中间件
Hertz中间件主要分为服务端中间件和客户端中间件两大类 。
服务端中间件
- Hertz 服务端中间件是 HTTP 请求-响应周期中的一个函数,提供了一种方便的机制来检查和过滤进入应用程序的 HTTP 请求, 例如记录每个请求或者启用CORS。
- 中间件可以在请求更深入地传递到业务逻辑之前或之后执行:
- 中间件可以在请求到达业务逻辑之前执行,比如执行身份认证和权限认证,当中间件只有初始化(pre-handle)相关逻辑,且没有和 real handler 在一个函数调用栈中的需求时,中间件中可以省略掉最后的.Next。
- 中间件也可以在执行过业务逻辑之后执行,比如记录响应时间和从异常中恢复。如果在业务 handler 处理之后有其它处理逻辑( post-handle ),或对函数调用链(栈)有强需求,则必须显式调用.Next。
实现一个中间件
func MyMiddleware() app.HandlerFunc {
return func(ctx context.Context, c *app.RequestContext) {
// pre-handle
// ...
c.Next(ctx)
// post-handle
// ...
}
}
中间件会按定义的先后顺序依次执行,如果想快速终止中间件调用,可以使用以下方法,注意当前中间件仍将执行。
- Abort():终止后续调用
- AbortWithMsg(msg string, statusCode int):终止后续调用,并设置 response中body,和状态码
- AbortWithStatus(code int):终止后续调用,并设置状态码
使用中间件
- 对全局路由生效则在服务器初始化之后就调用
h.Use(中间件函数) - 路由组中间件则对路由组调用Use()
- 对单个路由生效则在请求时第二个参数进行改动:
h.GET("/path", append(PathMiddleware(),
func(ctx context.Context, c *app.RequestContext) {
c.String(http.StatusOK, "path")
})...)
客户端中间件
Hertz Client
- 创建客户端:
c, err := client.NewClient()
- 发送get请求:
status, body, err := c.Get(context.Background(), nil, "http://www.example.com")
- 发送post请求:
var postArgs protocol.Args
postArgs.Set("key","value")//设置post参数,为键值对形式
status, body, err := c.Post(context.Background(), nil, "http://www.example.com", &postArgs)
上面两例中nil处可以传入一个字节数组,可以将数据放到字节数组里面复用
5.Hertz代码生成工具
Hertz提供了代码生成工具Hz,通过定义IDL(interface description language)文件即可生成对应的基础服务代码。