优雅停机与请求超时:http.Server 配置

本章解决什么问题:直接 kill 进程会掐断正在处理的请求,可能丢数据、留脏状态。优雅停机让服务“先停止接收新请求、把手头请求处理完再退出”;超时配置则防止慢请求无限拖住连接。官方文档 gin-gonic.com 的优雅停机示例与本例思路一致。

用 http.Server 包裹引擎

gin 的引擎本身就是 http.Handler,可以直接交给 http.Server,从而获得标准库的完整超时与关闭能力:

package main
import (
	"context"
	"errors"
	"log"
	"net/http"
	"os"
	"os/signal"
	"syscall"
	"time"

	"github.com/gin-gonic/gin"
)

func main() {
	r := gin.Default()
	r.GET("/ping", func(c *gin.Context) { c.String(200, "pong") })
	srv := &http.Server{
		Addr:              ":8080",
		Handler:           r,
		ReadHeaderTimeout: 5 * time.Second,
	}
	go func() {
		err := srv.ListenAndServe()
		if err != nil && !errors.Is(err, http.ErrServerClosed) {
			log.Fatalf("listen: %v", err)
		}
	}()
	ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
	defer stop()
	<-ctx.Done() // 收到 Ctrl+C 或 SIGTERM

	shutdownCtx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
	defer cancel()
	if err := srv.Shutdown(shutdownCtx); err != nil {
		log.Fatalf("shutdown: %v", err)
	}
	log.Println("server exited gracefully")
}

流程拆解:ListenAndServe 放 goroutine;主协程等信号;收到信号后用带超时的 context 调 srv.Shutdown——它先停止接收新连接,再等待存量请求完成,超过 10 秒则强制返回。若服务持有数据库连接池、后台任务等资源,可在 Shutdown 前后统一清理,或注册 srv.RegisterOnShutdown 回调,保证资源释放也走同一流程。

http.Server 的四个超时

  • ReadHeaderTimeout:读请求头超时,防御慢连接攻击,务必设置;
  • ReadTimeout:读取整个请求体的时间上限;
  • WriteTimeout:从请求头读完到响应写完的时间上限;
  • IdleTimeout:keep-alive 空闲连接的超时。

注意 WriteTimeout 是“一整个请求的总预算”,长任务接口可能超时被断,这正是触发业务超时设计的信号。四个值都设 0 表示不限时:本地调试可以宽松,生产环境务必全部收紧。

请求级超时

服务端超时只保护连接,业务自身还要防“依赖下游慢”。两种常用做法:

  • 用 http.TimeoutHandler 包住引擎:srv.Handler = http.TimeoutHandler(r, 5*time.Second, "timeout"),超时自动回 503;
  • 在 handler 里基于请求上下文派生超时:ctx, cancel := context.WithTimeout(c.Request.Context(), 2*time.Second),做数据库调用、调用下游时都传这个 ctx,让取消信号一路传递。

关键点

  • gin 引擎就是 http.Handler,直接嵌入 http.Server 即获得超时与优雅关闭能力。
  • 优雅停机 = 信号监听 + Shutdown:先拒新请求,再等旧请求办完,最后兜底强制退出。
  • 连接超时与业务超时要各司其职:前者防慢连接,后者用 context 取消链路。
  • 客户端断连时 c.Request.Context() 会自动取消,长任务要监听它而不是闷头跑完。
  • 超时与停机参数适合交给配置管理(见第 21 章),不同环境各有一份而不改代码。

小结

优雅停机解决“怎么退”,超时配置解决“怎么限”,两者合起来就是生产级服务的生命周期管理。至此,从环境搭建到运行治理的 Gin 主线已经贯通。

笔记加载中…