Go优雅退出 关闭http.Server

优雅退出:指HTTP服务在接到用户的退出指令后,停止接受新请求,对进行中的请求处理完成后再退出。

如下代码,main中启动http.ListenAndServe,在goroutinesignal.Notify监听退出信号,接口里sleep n秒模拟请求处理中。

测试:本地请求curl http://localhost:8080后,马上Ctrl + C。分别在处理函数里 sleep 3秒、8秒、20秒。看看区别。

这里有一个需要注意的点是,Shutdown在调用后,ListenAndServe立马返回,如果我们把ListenAndServe放在主函数main里,程序直接退出,无法等待Shutdown执行完成,参考Golang http.Server安全退出:容易被误用的Shutdown()方法

package main

import (
	"context"
	"log"
	"net/http"
	"os"
	"os/signal"
	"syscall"
	"time"
)

type Engine struct{}

func (engine *Engine) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	time.Sleep(time.Second * 20)
	w.WriteHeader(200)
	w.Write([]byte("ok"))
}

func main() {
	engine := new(Engine)
	server := &http.Server{
		Addr:         ":8080",
		Handler:      engine,
		ReadTimeout:  5 * time.Second,
		WriteTimeout: 5 * time.Second,
	}

	// http服务
	go func() {
		log.Println("HTTP服务启动", "http://localhost"+server.Addr)
		if err := server.ListenAndServe(); err != nil && err != http.ErrServerClosed {
			log.Println(err)
			os.Exit(0)
		}
		log.Println("HTTP服务关闭请求")
	}()

	// 监听信号 优雅退出http服务
	Watch(func() error {
		ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
		defer cancel()
		return server.Shutdown(ctx)
	})

	log.Println("程序退出")
}

// 监听信号
func Watch(fns ...func() error) {
	// 程序无法捕获信号 SIGKILL 和 SIGSTOP (终止和暂停进程),因此 os/signal 包对这两个信号无效。
	ch := make(chan os.Signal, 1)
	signal.Notify(ch, syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT, syscall.SIGUSR1, syscall.SIGUSR2)

	// 阻塞
	s := <-ch
	close(ch)
	log.Println("接收到信号", s.String(), "执行关闭函数")
	for i := range fns {
		if err := fns[i](); err != nil {
			log.Println(err)
		}
	}
	log.Println("关闭函数执行完成")
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
你可以使用 `context` 包来关闭 `go qidong()` 协程。以下是示例代码: ```go package main import ( "context" "fmt" "net/http" "time" "github.com/gin-gonic/gin" ) func qidong(ctx context.Context) error { gin.SetMode(gin.ReleaseMode) gin.DisableConsoleColor() router := gin.Default() //router.Use(log.Logger()) router.GET("/", SlowHandler) server := &http.Server{ Addr: ":8081", Handler: router, ReadTimeout: 10 * time.Second, WriteTimeout: 10 * time.Second, MaxHeaderBytes: 1 << 20, } go func() { if err := server.ListenAndServe(); err != nil && err != http.ErrServerClosed { fmt.Printf("listen: %s\n", err) } }() <-ctx.Done() // 等待接收关闭信号 ctxShutDown, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer func() { cancel() }() if err := server.Shutdown(ctxShutDown); err != nil { fmt.Printf("Server Shutdown Failed:%s\n", err) return err } fmt.Println("Server exiting") return nil } func main() { ctx, cancel := context.WithCancel(context.Background()) defer func() { cancel() }() go qidong(ctx) // 等待 10 秒钟,然后发送一个关闭信号给协程 time.Sleep(10 * time.Second) fmt.Println("发送关闭信号") cancel() // 等待 1 秒钟,观察协程是否被正常关闭。 time.Sleep(1 * time.Second) fmt.Println("main 函数退出") } func SlowHandler(c *gin.Context) { time.Sleep(2 * time.Second) c.JSON(http.StatusOK, gin.H{"message": "slow response"}) } ``` 在上面的示例代码中,我们使用 `context` 包创建了一个 `ctx` 对象,并把它传递给 `qidong()` 协程。在协程函数中,我们使用 `select` 语句监听 `ctx.Done()` 通道,一旦收到关闭信号,就会退出协程。 在 `main()` 函数中,我们等待了 10 秒钟,然后发送一个关闭信号给协程。协程函数会在执行过程中不断地检查 `ctx.Done()` 通道,一旦收到关闭信号,就会退出协程。在 `main()` 函数中,我们等待了 1 秒钟,观察协程是否被正常关闭。在协程函数中,我们还使用了 `server.Shutdown()` 方法来关闭 HTTP 服务,以确保服务正常退出

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值