实时端点OS信号处理:服务器收SIGTERM时如何应对忙碌请求?
Gin框架下捕获SIGTERM信号优雅处理请求并关闭数据库连接
核心处理逻辑
- 监听SIGTERM信号,触发优雅关闭流程
- 先停止服务器接收新请求,等待正在处理的请求完成(可设置超时上限)
- 待请求处理完毕后,关闭所有数据库连接
- 最终安全退出程序
完整代码示例
package main import ( "context" "log" "net/http" "os" "os/signal" "syscall" "time" "github.com/gin-gonic/gin" "gorm.io/driver/mysql" "gorm.io/gorm" ) var db *gorm.DB // 初始化数据库连接(示例用GORM,可替换为你的数据库驱动) func initDB() { dsn := "user:password@tcp(127.0.0.1:3306)/dbname?charset=utf8mb4&parseTime=True&loc=Local" var err error db, err = gorm.Open(mysql.Open(dsn), &gorm.Config{}) if err != nil { log.Fatalf("数据库连接失败: %v", err) } sqlDB, err := db.DB() if err != nil { log.Fatalf("获取底层sql连接失败: %v", err) } // 配置连接池参数(可选) sqlDB.SetMaxOpenConns(10) sqlDB.SetMaxIdleConns(5) } func main() { initDB() r := gin.Default() // 模拟处理大数据的耗时端点 r.GET("/big-data", func(c *gin.Context) { log.Println("开始处理大数据请求") // 模拟5秒耗时操作 time.Sleep(5 * time.Second) c.JSON(http.StatusOK, gin.H{"message": "大数据处理完成"}) log.Println("大数据请求处理完成") }) srv := &http.Server{ Addr: ":8080", Handler: r, } // 启动服务器(后台goroutine) go func() { if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed { log.Fatalf("监听端口失败: %s", err) } }() // 监听SIGTERM信号 quit := make(chan os.Signal, 1) signal.Notify(quit, syscall.SIGTERM) <-quit log.Println("收到SIGTERM信号,启动优雅关闭流程") // 设置上下文,给活跃请求最多30秒处理时间 ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) defer cancel() // 停止接收新请求,等待活跃请求处理完成 if err := srv.Shutdown(ctx); err != nil { log.Fatalf("服务器优雅关闭失败: %v", err) } log.Println("服务器已停止接收新请求,等待活跃请求收尾") // 关闭数据库连接 sqlDB, err := db.DB() if err != nil { log.Fatalf("获取sql连接实例失败: %v", err) } if err := sqlDB.Close(); err != nil { log.Fatalf("数据库连接关闭失败: %v", err) } log.Println("所有数据库连接已关闭") log.Println("服务器已安全退出") }
关键细节说明
- 信号监听:通过
signal.Notify捕获SIGTERM信号,避免直接强制终止程序 - 优雅关闭服务器:
srv.Shutdown(ctx)会自动停止端口监听、拒绝新请求,但会保留当前活跃连接直到处理完成或超时 - 数据库连接时机:必须等所有请求处理完毕后再关闭数据库,否则正在执行的数据库操作会直接报错
- 超时控制:通过
context.WithTimeout设置最大等待时间,防止因个别超长耗时请求导致程序无法退出
内容的提问来源于stack exchange,提问作者GurbaniX
相关产品推荐
相关产品推荐

