Hertz v0.6.0 版本发布

Hertz 0.6.0 版本中,除了常规迭代优化之外,我们还带来了多个重要 feature。

支持 HTTP Trailer

在 Hertz v0.6.0 版本中,我们支持了 HTTP Trailer 的编码和解析。

https://github.com/cloudwego/hertz-examples/tree/main/trailer

  • 写 Trailer
// server 端
func handler(ctx context.Context, c *app.RequestContext){
    c.Response.Header.Trailer().Set("Hertz", "Good")
}

// client 端
req.Header.Trailer().Set("Hertz", "Good")
  • 读 Trailer
// server 端
func handler(ctx context.Context, c *app.RequestContext){
    c.Request.Header.Trailer().Get("Hertz")
}

// client 端
resp.Header.Trailer().Get("Hertz")

HTTP/1.1 支持 Response Writer 劫持

在 Hertz v0.6.0 版本中,我们扩展了 HTTP/1.1 写请求的方式,在原来写请求流程的基础之上,支持用户在业务 handler/中间件中劫持 Response Writer,实现更加灵活的写请求方式。 简单来说,原来所有的“底层写”逻辑统一放到 handler/中间件返回之后,这个带来两个比较明显的局限性:

  1. 用户无法控制请求真正 flush 到对端的时机
  2. 针对 chunk 方式增量产生数据 & 实时写到对端的场景,在老的架构之上用法相对复杂,限制相对较多

基于此我们扩展出一套能够提供自行 flush 请求头和请求体的能力,同时提供了一个支持用户按需发送 chunk 数据的 Writer。详细实现参考:https://github.com/cloudwego/hertz/pull/610

主要变更

  1. 增加了一个扩展 Writer 的接口定义,实现了这个接口的 Writer 都可以用作劫持 Response Writer:

    type ExtWriter interface {
            io.Writer
            Flush() error
    
            // Finalize will be called by framework before the writer is released.
            // Implementations must guarantee that Finalize is safe for multiple calls.
            Finalize() error
    }
    
  2. 提供了一个实现了上述接口的 Chunk Writer(有类似需求都可以参考这个来实现):chunkedBodyWrite

  3. HTTP/1.1 具体写请求的地方针对被劫持了 Writer 的 Response 写操作做了对应的处理(跳过默认写请求逻辑),最后调用ExtWriter接口的Finalize()方法完成一次请求写回

使用方法

如上,Hertz 提供了一个默认的ExtWriter实现满足用户在 handler/中间件中的主动 flush 需求,使用方式也非常简单:

h.GET("/flush/chunk", func(ctx context.Context, c *app.RequestContext) {
        // Hijack the writer of response
        c.Response.HijackWriter(resp.NewChunkedBodyWriter(&c.Response, c.GetWriter()))

        for i := 0; i < 10; i++ {
                c.Write([]byte(fmt.Sprintf("chunk %d: %s", i, strings.Repeat("hi~", i)))) // nolint: errcheck
                c.Flush()                                                                 // nolint: errcheck
                time.Sleep(200 * time.Millisecond)
        }
})

脚手架使用优化 & 最佳实践

在 hz v0.6.0 版本中,我们对生成代码的组织结构进行一系列的优化,从而可生成更加灵活的代码组织结构

主要优化

  • new 命令支持 “router_dir” 选项,并配合已有的 “handler_dir”、“model_dir”,可完全自定义 IDL 生成产物的路径;并且会将这些自定义选项持久化到 “.hz” 文件中,可在 update 时自动读取,减少命令的复杂度
  • 增加向上搜索 “go.mod” 文件的能力,从而使得 hertz 在作为一个子项目时可以和其他项目共享同一个 “go module”
  • 增加 “handler” 中引用第三方 IDL 产物的能力,可将 IDL 产物放到第三方仓库单独维护,使其不在项目目录中存放,进一步增强 IDL 管理能力

最佳实践

我们利用 “hz v0.6.0” 重写了 “biz-demo/easy-note",主要利用了如下 hz 的特性

  • 利用 “hz client” 的能力,基于 IDL 生成访问 “api server” 的 hertz client 调用代码
  • 利用自定义 “router_dir”、 “handler_dir”、“model_dir” 选项,重新调整 “api server” 代码的组织结构,去掉 “biz” 目录的限制
  • 利用 “向上搜索 go.mod” 的能力,使得 “api server” 可以作为 “easy-note” 的子项目共享同一个 “go module”
  • 利用 “handler 引用第三方 IDL 产物” 的能力并配合 “hz model” 的能力,使得 IDL 产物单独存到到 “easy-note” 项目里,而并不存放到 “api server” 子项目里

完整 Release Note

完整的 Release Note 可以参考: