Hooks

钩子函数(Hooks)是一个通用的概念,表示某事件触发时所伴随的操作。

Hertz 提供了全局的 Hook 注入能力,用于在服务触发启动后退出前注入自己的处理逻辑。

StartHook

StartHook 在 Hertz 当中表示服务触发启动后需调用的函数,使用 CtxErrCallback 类型表示。Hertz 使用 OnRun 属性存储 StartHook 列表。

// CtxErrCallback 参见下方其函数签名
OnRun []CtxErrCallback

触发 Server 启动后,框架会按函数声明顺序依次调用所有的 StartHook 函数,完成调用之后,才会正式开始端口监听,如果发生错误,则立刻终止服务。

函数签名:

type CtxErrCallback func(ctx context.Context) error

示例代码:

package main

import (
    "context"

    "github.com/cloudwego/hertz/pkg/app"
    "github.com/cloudwego/hertz/pkg/app/server"
    "github.com/cloudwego/hertz/pkg/common/hlog"
    "github.com/cloudwego/hertz/pkg/common/utils"
    "github.com/cloudwego/hertz/pkg/protocol/consts"
)

func main() {
    h := server.Default()

    h.OnRun = append(h.OnRun, func(ctx context.Context) error {
        hlog.Info("run the first start hook")
        return nil
    })
    h.OnRun = append(h.OnRun, func(ctx context.Context) error {
        hlog.Info("run the second start hook")
        return nil
    })
    h.OnRun = append(h.OnRun, func(ctx context.Context) error {
        hlog.Info("run the third start hook")
        return nil
    })

    h.GET("/ping", func(c context.Context, ctx *app.RequestContext) {
        ctx.JSON(consts.StatusOK, utils.H{"ping": "pong"})
    })

    h.Spin()
}

提示:启动服务,将在控制台顺序打印三个 StartHook 函数的日志。

main.go:17: [Info] run the first start hook
main.go:21: [Info] run the second start hook
main.go:25: [Info] run the third start hook

ShutdownHook

ShutdownHook 在 Hertz 当中表示服务退出前需调用的函数,使用 CtxCallback 类型表示。Hertz 使用 OnShutdown 属性存储 ShutdownHook 列表。

Server 退出前,框架会并发地调用所有声明的 ShutdownHook 函数,并且可以通过 server.WithExitWaitTime配置最大等待时长,默认为5秒,如果超时,则立刻终止服务。

ShutdownHook 的调用本质上是 Hertz 优雅退出 的一环。

函数签名:

type CtxCallback func(ctx context.Context)

示例代码1:

package main

import (
    "context"

    "github.com/cloudwego/hertz/pkg/app"
    "github.com/cloudwego/hertz/pkg/app/server"
    "github.com/cloudwego/hertz/pkg/common/hlog"
    "github.com/cloudwego/hertz/pkg/common/utils"
    "github.com/cloudwego/hertz/pkg/protocol/consts"
)

func main() {
    h := server.Default()

    h.OnShutdown = append(h.OnShutdown, func(ctx context.Context) {
        hlog.Info("run the first shutdown hook")
    })
    h.OnShutdown = append(h.OnShutdown, func(ctx context.Context) {
        hlog.Info("run the second shutdown hook")
    })
    h.OnShutdown = append(h.OnShutdown, func(ctx context.Context) {
        hlog.Info("run the third shutdown hook")
    })

    h.GET("/ping", func(c context.Context, ctx *app.RequestContext) {
        ctx.JSON(consts.StatusOK, utils.H{"ping": "pong"})
    })

    h.Spin()
}

提示:终止服务,将在控制台乱序打印三个 ShutdownHook 函数的日志。

hertz.go:77: [Info] HERTZ: Begin graceful shutdown, wait at most num=5 seconds...
main.go:22: [Info] run the third shutdown hook
main.go:16: [Info] run the first shutdown hook
main.go:19: [Info] run the second shutdown hook
engine.go:279: [Info] HERTZ: Execute OnShutdownHooks finish

示例代码2:

package main

import (
	"context"
    "time"

	"github.com/cloudwego/hertz/pkg/app"
	"github.com/cloudwego/hertz/pkg/app/server"
	"github.com/cloudwego/hertz/pkg/common/hlog"
	"github.com/cloudwego/hertz/pkg/common/utils"
	"github.com/cloudwego/hertz/pkg/protocol/consts"
)

func main() {
	h := server.Default(server.WithExitWaitTime(time.Second * 2))

	h.OnShutdown = append(h.OnShutdown, func(ctx context.Context) {
		hlog.Info("run shutdown hook")
		time.Sleep(time.Second * 5)
	})

	h.GET("/ping", func(c context.Context, ctx *app.RequestContext) {
		ctx.JSON(consts.StatusOK, utils.H{"ping": "pong"})
	})

	h.Spin()
}

提示:终止服务时,因为钩子函数执行时间超过2秒,打印超时日志。

hertz.go:77: [Info] HERTZ: Begin graceful shutdown, wait at most num=2 seconds...
main.go:17: [Info] run shutdown hook
engine.go:276: [Info] HERTZ: Execute OnShutdownHooks timeout: error=context deadline exceeded

最后修改 April 18, 2023 : docs(wip): request context handler (36ea2d4)