Hertz基本使用 | 青训营笔记

887 阅读5分钟

这是我参与「第五届青训营 」笔记创作活动的第 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 提供了 GETPOSTPUTDELETEAnyPATCHHEADOPTIONSHandleStaticFile/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)文件即可生成对应的基础服务代码。