From a0b12238187dc19e76fbfcd95f9e5d7a3fe8e505 Mon Sep 17 00:00:00 2001 From: tbphp Date: Mon, 14 Jul 2025 11:13:44 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E4=BC=98=E9=9B=85?= =?UTF-8?q?=E9=80=80=E5=87=BA=E8=B6=85=E6=97=B6=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docker-compose.yml | 1 + internal/app/app.go | 23 +++++++++++++++++++---- internal/config/manager.go | 6 ++++++ 3 files changed, 26 insertions(+), 4 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index ed0655d..0e1783f 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -10,6 +10,7 @@ services: env_file: - .env restart: always + stop_grace_period: ${SERVER_GRACEFUL_SHUTDOWN_TIMEOUT:-60}s depends_on: mysql: condition: service_healthy diff --git a/internal/app/app.go b/internal/app/app.go index e0a8af8..1abda8d 100644 --- a/internal/app/app.go +++ b/internal/app/app.go @@ -169,10 +169,26 @@ func (a *App) Start() error { func (a *App) Stop(ctx context.Context) { logrus.Info("Shutting down server...") - if err := a.httpServer.Shutdown(ctx); err != nil { - logrus.Errorf("Server forced to shutdown: %v", err) - } + serverConfig := a.configManager.GetEffectiveServerConfig() + totalTimeout := time.Duration(serverConfig.GracefulShutdownTimeout) * time.Second + // 动态计算 HTTP 关机超时时间,为后台服务固定预留 5 秒 + httpShutdownTimeout := totalTimeout - 5*time.Second + + // 为 HTTP 服务器的优雅关闭创建一个独立的 context + httpShutdownCtx, cancelHttpShutdown := context.WithTimeout(context.Background(), httpShutdownTimeout) + defer cancelHttpShutdown() + + logrus.Debugf("Attempting to gracefully shut down HTTP server (max %v)...", httpShutdownTimeout) + if err := a.httpServer.Shutdown(httpShutdownCtx); err != nil { + logrus.Debugf("HTTP server graceful shutdown timed out as expected, forcing remaining connections to close.") + if closeErr := a.httpServer.Close(); closeErr != nil { + logrus.Errorf("Error forcing HTTP server to close: %v", closeErr) + } + } + logrus.Info("HTTP server has been shut down.") + + // 使用原始的总超时 context 继续关闭其他后台服务 stoppableServices := []func(context.Context){ a.cronChecker.Stop, a.leaderLock.Stop, @@ -192,7 +208,6 @@ func (a *App) Stop(ctx context.Context) { }(stopFunc) } - // Wait for all services to stop, or for the context to be done. done := make(chan struct{}) go func() { wg.Wait() diff --git a/internal/config/manager.go b/internal/config/manager.go index f187561..6117b42 100644 --- a/internal/config/manager.go +++ b/internal/config/manager.go @@ -164,6 +164,12 @@ func (m *Manager) Validate() error { validationErrors = append(validationErrors, "AUTH_KEY is required and cannot be empty") } + // Validate GracefulShutdownTimeout and reset if necessary + if m.config.Server.GracefulShutdownTimeout < 10 { + logrus.Warnf("SERVER_GRACEFUL_SHUTDOWN_TIMEOUT value %ds is too short, resetting to minimum 10s.", m.config.Server.GracefulShutdownTimeout) + m.config.Server.GracefulShutdownTimeout = 10 + } + if len(validationErrors) > 0 { logrus.Error("Configuration validation failed:") for _, err := range validationErrors {