Hertz的优雅退出

Hertz 停止服务时提供的优雅退出功能。

Hertz 支持优雅退出,优雅退出过程如下:

  1. 设置 engine 状态为 closed

  2. 顺序非阻塞触发回调函数 []OnShutDown(与标准包 net/http 一致),Select 等待回调函数执行完成或者超时返回

  3. 注册中心注销对应服务

  4. 关闭网络库的信号监听

  5. 对处于关闭过程中的请求回包统一带上 Connection:Close header

  6. Select等待业务协程退出:

    1. 对于 netpoll 网络库,开启默认 1s(netpoll 中设置,暂时不可更改)的 ticker,定时查看 active conn(业务 handle 退出且连接不处于阻塞读状态)是否为 0;对于 go net 网络库,则关闭监听,不对连接做处理。
    2. 等待超时时间为 ExitWaitTime 的 context 触发,默认 5s

如需修改等待超时时间,可通过 server.WithExitWaitTime() 进行配置。

如需注册退出 hook 函数,可通过获取到 Engine 后进行注册:

h.Engine.OnShutdown = append(h.Engine.OnShutdown, shutDownFunc)

例如

package main

import (
	"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 main

import (
	"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 := <-sigChan
			fmt.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 nil
			default:
				// 其他信号,默认优雅退出
				return nil
			}
		},
	)

	h.Spin()
}

当自定义信号处理函数返回 error 时 Hertz 会立即退出,其他情况下则会优雅退出。

posted @   厚礼蝎  阅读(4)  评论(0编辑  收藏  举报
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 25岁的心里话
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
历史上的今天:
2023-02-28 shell中产生随机字符串的方法
点击右上角即可分享
微信分享提示