Skip to content

应用生命周期管理

概述

应用生命周期管理定义了 Go 应用从启动到关闭的完整流程。核心目标是确保资源正确初始化和释放,支持优雅关闭。

生命周期阶段

完整生命周期流程

App 容器模式

App 结构定义

使用 App 容器管理所有依赖:

go
// app/app.go
package app

import (
    "context"
    "fmt"

    "your-project/clients/storage"
    "your-project/config"
    "your-project/services"
    "gorm.io/gorm"
)

// App 应用容器
type App struct {
    // 配置
    config *config.Config

    // 基础设施
    db *gorm.DB

    // Clients
    storageClient *storage.Client
    emailClient   *email.Client

    // Services
    userService    *services.UserService
    productService *services.ProductService
    orderService   *services.OrderService
}

// NewApp 创建应用实例
func NewApp() *App {
    return &App{}
}

应用初始化

go
// Init 初始化应用
func (a *App) Init(ctx context.Context, cfg *config.Config) error {
    a.config = cfg

    // 1. 初始化数据库
    if err := a.initDatabase(ctx); err != nil {
        return fmt.Errorf("failed to init database: %w", err)
    }

    // 2. 初始化 Clients
    if err := a.initClients(); err != nil {
        return fmt.Errorf("failed to init clients: %w", err)
    }

    // 3. 初始化 Services
    a.initServices()

    return nil
}

// initDatabase 初始化数据库连接
func (a *App) initDatabase(ctx context.Context) error {
    db, err := gorm.Open(mysql.Open(a.config.Database.DSN()), &gorm.Config{
        Logger: logger.Default.LogMode(logger.Info),
    })
    if err != nil {
        return err
    }

    // 配置连接池
    sqlDB, _ := db.DB()
    sqlDB.SetMaxIdleConns(10)
    sqlDB.SetMaxOpenConns(100)
    sqlDB.SetConnMaxLifetime(time.Hour)

    a.db = db
    return nil
}

// initClients 初始化 Clients
func (a *App) initClients() error {
    // 对象存储 Client
    storageClient, err := storage.NewClient(&a.config.Storage)
    if err != nil {
        return fmt.Errorf("failed to init storage client: %w", err)
    }
    a.storageClient = storageClient

    // 邮件 Client
    emailClient, err := email.NewClient(&a.config.Email)
    if err != nil {
        return fmt.Errorf("failed to init email client: %w", err)
    }
    a.emailClient = emailClient

    return nil
}

// initServices 初始化 Services
func (a *App) initServices() {
    // 注入依赖创建 Services
    a.userService = services.NewUserService(a.db, a.emailClient)
    a.productService = services.NewProductService(a.db, a.storageClient)
    a.orderService = services.NewOrderService(a.db, a.userService, a.productService)
}

// GetUserService 获取 UserService
func (a *App) GetUserService() *services.UserService {
    return a.userService
}

// GetProductService 获取 ProductService
func (a *App) GetProductService() *services.ProductService {
    return a.productService
}

// GetOrderService 获取 OrderService
func (a *App) GetOrderService() *services.OrderService {
    return a.orderService
}

主程序入口

main.go 实现

go
// main.go
package main

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

    "github.com/labstack/echo/v4"
    "github.com/labstack/echo/v4/middleware"

    "your-project/app"
    "your-project/config"
    "your-project/httpapi/routes"
)

func main() {
    // 1. 加载配置
    cfg, err := config.Load("config.toml")
    if err != nil {
        log.Fatalf("Failed to load config: %v", err)
    }

    // 2. 创建 App 容器
    application := app.NewApp()

    // 3. 初始化应用
    ctx := context.Background()
    if err := application.Init(ctx, cfg); err != nil {
        log.Fatalf("Failed to init app: %v", err)
    }

    // 4. 创建 HTTP 服务器
    e := echo.New()

    // 5. 配置中间件
    e.Use(middleware.Logger())
    e.Use(middleware.Recover())
    e.Use(middleware.CORS())

    // 6. 注册路由
    routes.Register(e, application)

    // 7. 启动服务器(非阻塞)
    go func() {
        addr := fmt.Sprintf(":%d", cfg.Server.Port)
        if err := e.Start(addr); err != nil && err != http.ErrServerClosed {
            log.Fatalf("Failed to start server: %v", err)
        }
    }()

    log.Printf("Server started on port %d", cfg.Server.Port)

    // 8. 等待中断信号
    quit := make(chan os.Signal, 1)
    signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
    <-quit

    log.Println("Shutting down server...")

    // 9. 优雅关闭
    shutdownCtx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
    defer cancel()

    if err := e.Shutdown(shutdownCtx); err != nil {
        log.Printf("Server shutdown error: %v", err)
    }

    // 10. 清理资源
    if err := application.Close(ctx); err != nil {
        log.Printf("App cleanup error: %v", err)
    }

    log.Println("Server stopped")
}

路由注册

go
// httpapi/routes/routes.go
package routes

import (
    "github.com/labstack/echo/v4"

    "your-project/app"
    "your-project/httpapi/handlers"
)

// Register 注册所有路由
func Register(e *echo.Echo, app *app.App) {
    // API 分组
    api := e.Group("/api/v1")

    // 用户路由
    userHandler := handlers.NewUserHandler(app)
    api.POST("/users", userHandler.Create)
    api.GET("/users", userHandler.List)
    api.GET("/users/:id", userHandler.GetByID)
    api.PATCH("/users/:id", userHandler.Update)
    api.DELETE("/users/:id", userHandler.Delete)

    // 商品路由
    productHandler := handlers.NewProductHandler(app)
    api.POST("/products", productHandler.Create)
    api.GET("/products", productHandler.List)
    api.GET("/products/:id", productHandler.GetByID)

    // 订单路由
    orderHandler := handlers.NewOrderHandler(app)
    api.POST("/orders", orderHandler.Create)
    api.GET("/orders", orderHandler.List)
    api.GET("/orders/:id", orderHandler.GetByID)
}

优雅关闭

关闭流程

go
// app/app.go
// Close 优雅关闭应用
func (a *App) Close(ctx context.Context) error {
    log.Println("Closing application resources...")

    // 1. 关闭 Clients
    if a.storageClient != nil {
        if err := a.storageClient.Close(); err != nil {
            log.Printf("Failed to close storage client: %v", err)
        }
    }

    if a.emailClient != nil {
        if err := a.emailClient.Close(); err != nil {
            log.Printf("Failed to close email client: %v", err)
        }
    }

    // 2. 关闭数据库连接
    if a.db != nil {
        sqlDB, _ := a.db.DB()
        if err := sqlDB.Close(); err != nil {
            log.Printf("Failed to close database: %v", err)
        }
    }

    log.Println("Application resources closed")
    return nil
}

信号处理

go
// ✅ 正确 - 处理多个信号
quit := make(chan os.Signal, 1)
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)

select {
case <-quit:
    log.Println("Received shutdown signal")
case <-ctx.Done():
    log.Println("Context canceled")
}

// 优雅关闭,等待正在处理的请求完成
shutdownCtx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()

if err := e.Shutdown(shutdownCtx); err != nil {
    log.Fatalf("Server forced to shutdown: %v", err)
}

健康检查

健康检查端点

go
// httpapi/handlers/health.go
package handlers

import (
    "net/http"

    "github.com/labstack/echo/v4"
    "your-project/app"
)

type HealthHandler struct {
    app *app.App
}

func NewHealthHandler(app *app.App) *HealthHandler {
    return &HealthHandler{app: app}
}

// Health 健康检查
func (h *HealthHandler) Health(c echo.Context) error {
    // 检查数据库连接
    sqlDB, _ := h.app.GetDB().DB()
    if err := sqlDB.Ping(); err != nil {
        return c.JSON(http.StatusServiceUnavailable, map[string]interface{}{
            "status": "unhealthy",
            "error":  "database connection failed",
        })
    }

    return c.JSON(http.StatusOK, map[string]interface{}{
        "status": "healthy",
    })
}

// Readiness 就绪检查
func (h *HealthHandler) Readiness(c echo.Context) error {
    // 检查应用是否完全初始化
    if h.app.GetUserService() == nil {
        return c.JSON(http.StatusServiceUnavailable, map[string]interface{}{
            "status": "not ready",
        })
    }

    return c.JSON(http.StatusOK, map[string]interface{}{
        "status": "ready",
    })
}

注册健康检查路由

go
// routes/routes.go
func Register(e *echo.Echo, app *app.App) {
    // 健康检查(不需要认证)
    healthHandler := handlers.NewHealthHandler(app)
    e.GET("/health", healthHandler.Health)
    e.GET("/ready", healthHandler.Readiness)

    // API 路由...
}

配置热重载

监听配置变化

go
// ✅ 正确 - 使用 fsnotify 监听配置文件
import "github.com/fsnotify/fsnotify"

func watchConfig(configPath string, reloadFunc func()) {
    watcher, err := fsnotify.NewWatcher()
    if err != nil {
        log.Fatal(err)
    }
    defer watcher.Close()

    go func() {
        for {
            select {
            case event := <-watcher.Events:
                if event.Op&fsnotify.Write == fsnotify.Write {
                    log.Println("Config file changed, reloading...")
                    reloadFunc()
                }
            case err := <-watcher.Errors:
                log.Println("Watcher error:", err)
            }
        }
    }()

    watcher.Add(configPath)
    <-make(chan struct{})  // 阻塞
}

启动检查清单

应用启动时必须验证:

  • [ ] 配置文件加载成功
  • [ ] 所有必需的环境变量已设置
  • [ ] 数据库连接成功
  • [ ] 所有 Client 初始化成功
  • [ ] HTTP 服务器启动成功
  • [ ] 健康检查端点可访问

错误处理原则

启动时错误

go
// ✅ 正确 - 启动失败立即退出
if err := application.Init(ctx, cfg); err != nil {
    log.Fatalf("Failed to init app: %v", err)  // 使用 Fatalf 退出
}

// ❌ 错误 - 启动失败继续运行
if err := application.Init(ctx, cfg); err != nil {
    log.Printf("Failed to init app: %v", err)  // 仅记录日志
}

运行时错误

go
// ✅ 正确 - 运行时错误记录日志并返回错误响应
func (h *UserHandler) Create(c echo.Context) error {
    user, err := h.app.GetUserService().Create(ctx, req)
    if err != nil {
        log.Printf("Failed to create user: %v", err)
        return c.JSON(500, response.Fail(500, "创建失败"))
    }
    return c.JSON(201, response.Success(user))
}

相关文档