Hertz 停止服务时提供的优雅退出功能。
Hertz 支持优雅退出,优雅退出过程如下:
-
设置
engine
状态为closed
-
顺序非阻塞触发回调函数
[]OnShutDown
(与标准包 net/http 一致),Select
等待回调函数执行完成或者超时返回 -
注册中心注销对应服务
-
关闭网络库的信号监听
-
对处于关闭过程中的请求回包统一带上
Connection:Close
header -
Select等待业务协程退出:
- 对于 netpoll 网络库,开启默认 1s(netpoll 中设置,暂时不可更改)的
ticker
,定时查看active conn
(业务 handle 退出且连接不处于阻塞读状态)是否为 0;对于 go net 网络库,则关闭监听,不对连接做处理。 - 等待超时时间为
ExitWaitTime
的 context 触发,默认 5s
- 对于 netpoll 网络库,开启默认 1s(netpoll 中设置,暂时不可更改)的
如需修改等待超时时间,可通过 server.WithExitWaitTime()
进行配置。
如需注册退出 hook
函数,可通过获取到 Engine
后进行注册:
h.Engine.OnShutdown = append(h.Engine.OnShutdown, shutDownFunc)
例如
package mainimport ("context""fmt""github.com/cloudwego/hertz/pkg/app""github.com/cloudwego/hertz/pkg/app/server""github.com/cloudwego/hertz/pkg/common/utils""github.com/cloudwego/hertz/pkg/protocol/consts""time"
)func GroupMiddleware() app.HandlerFunc {return func(ctx context.Context, c *app.RequestContext) {fmt.Println("group middleware")c.Next(ctx)}
}func main() {h := server.Default(server.WithHostPorts("127.0.0.1:8888"),server.WithExitWaitTime(10*time.Second),)h.GET("/ping", func(ctx context.Context, c *app.RequestContext) {c.JSON(consts.StatusOK, utils.H{"message": "pong"})},)h.OnShutdown = append(h.OnShutdown, func(ctx context.Context) {fmt.Println("开始退出了1")}, func(ctx context.Context) {fmt.Println("开始退出了2")}, func(ctx context.Context) {fmt.Println("开始退出了3")},func(ctx context.Context) {fmt.Println("开始退出了4")},)h.Spin()
}
Hertz 使用 waitSignal
函数作为信号处理的默认实现方式,处理如下:
- 当接收到
SIGTERM
系统信号时触发立即退出。 - 当接收到
SIGHUP|SIGINT
系统信号时触发优雅退出。
当信号处理的默认实现方式无法满足需求时,可通过 SetCustomSignalWaiter
来自定义信号处理方式。
package mainimport ("context""fmt""github.com/cloudwego/hertz/pkg/app""github.com/cloudwego/hertz/pkg/app/server""github.com/cloudwego/hertz/pkg/common/utils""github.com/cloudwego/hertz/pkg/protocol/consts""os""os/signal""syscall""time"
)func GroupMiddleware() app.HandlerFunc {return func(ctx context.Context, c *app.RequestContext) {fmt.Println("group middleware")c.Next(ctx)}
}func main() {h := server.Default(server.WithHostPorts("127.0.0.1:8888"),server.WithExitWaitTime(10*time.Second),)h.GET("/ping", func(ctx context.Context, c *app.RequestContext) {c.JSON(consts.StatusOK, utils.H{"message": "pong"})},)// 设置自定义信号处理函数h.SetCustomSignalWaiter(func(errChan chan error) error {// 创建一个 channel 用于接收系统信号sigChan := make(chan os.Signal, 1)// 监听 SIGHUP、SIGINT 和 SIGTERM 信号signal.Notify(sigChan, syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM)// 等待信号sig := <-sigChanfmt.Printf("Received signal: %v\n", sig)// 根据信号类型决定退出行为switch sig {case syscall.SIGTERM:// 如果是 SIGTERM,返回 error 触发立即退出return fmt.Errorf("received SIGTERM, exiting immediately")case syscall.SIGHUP, syscall.SIGINT:// 如果是 SIGHUP 或 SIGINT,不返回 error,触发优雅退出fmt.Println("Received SIGHUP or SIGINT, starting graceful shutdown")return nildefault:// 其他信号,默认优雅退出return nil}},)h.Spin()
}
当自定义信号处理函数返回 error
时 Hertz 会立即退出,其他情况下则会优雅退出。