Skip to content

生命周期钩子

VEF Framework 基于 Uber FX 提供了应用生命周期管理。

生命周期阶段

  1. 构造阶段:创建和注入依赖
  2. 启动阶段:执行 OnStart 钩子
  3. 运行阶段:应用正常运行
  4. 停止阶段:执行 OnStop 钩子

注册生命周期钩子

使用 fx.Lifecycle

go
package services

import (
    "context"
    
    "go.uber.org/fx"
)

type CacheService struct {
    // ...
}

func NewCacheService(lc fx.Lifecycle) *CacheService {
    service := &CacheService{}
    
    lc.Append(fx.Hook{
        OnStart: func(ctx context.Context) error {
            // Initialize cache connection
            return service.connect()
        },
        OnStop: func(ctx context.Context) error {
            // Close cache connection
            return service.close()
        },
    })
    
    return service
}

使用 fx.Invoke

go
func RegisterLifecycleHooks(lc fx.Lifecycle, eventHandler *handlers.UserEventHandler) {
    lc.Append(fx.Hook{
        OnStart: func(ctx context.Context) error {
            // Register event handlers on start
            eventbus.Subscribe("user.created", eventHandler.OnUserCreated)
            eventbus.Subscribe("user.deleted", eventHandler.OnUserDeleted)
            return nil
        },
        OnStop: func(ctx context.Context) error {
            // Unsubscribe on stop
            eventbus.Unsubscribe("user.created", eventHandler.OnUserCreated)
            eventbus.Unsubscribe("user.deleted", eventHandler.OnUserDeleted)
            return nil
        },
    })
}

启动顺序控制

依赖顺序

go
// Services start in dependency order
// Database -> Cache -> EventBus -> HTTP Server

func NewApp() *fx.App {
    return fx.New(
        fx.Provide(
            NewDatabaseService,  // Starts first
            NewCacheService,     // Depends on database
            NewEventBus,         // Depends on cache
            NewHTTPServer,       // Starts last
        ),
    )
}

显式依赖

go
// Explicit dependency declaration
func NewHTTPServer(
    db *DatabaseService,    // Depends on database
    cache *CacheService,    // Depends on cache
) *HTTPServer {
    return &HTTPServer{db: db, cache: cache}
}

优雅关闭

配置关闭超时

go
app := fx.New(
    fx.Options(
        // ... providers
    ),
    fx.StopTimeout(30*time.Second), // Allow 30 seconds for graceful shutdown
)

处理关闭信号

go
func main() {
    app := fx.New(
        // ... options
    )
    
    // Start application
    startCtx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
    defer cancel()
    
    if err := app.Start(startCtx); err != nil {
        log.Fatal(err)
    }
    
    // Wait for interrupt signal
    quit := make(chan os.Signal, 1)
    signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
    <-quit
    
    // Graceful shutdown
    stopCtx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
    defer cancel()
    
    if err := app.Stop(stopCtx); err != nil {
        log.Error("Error during shutdown:", err)
    }
}

健康检查

实现健康检查

go
type HealthChecker struct {
    db    *gorm.DB
    redis *redis.Client
}

func NewHealthChecker(db *gorm.DB, redis *redis.Client) *HealthChecker {
    return &HealthChecker{db: db, redis: redis}
}

func (h *HealthChecker) Check() map[string]string {
    status := make(map[string]string)
    
    // Check database
    sqlDB, _ := h.db.DB()
    if err := sqlDB.Ping(); err != nil {
        status["database"] = "unhealthy"
    } else {
        status["database"] = "healthy"
    }
    
    // Check Redis
    if err := h.redis.Ping(context.Background()).Err(); err != nil {
        status["redis"] = "unhealthy"
    } else {
        status["redis"] = "healthy"
    }
    
    return status
}

注册健康检查端点

go
func RegisterHealthEndpoint(app *fiber.App, checker *HealthChecker) {
    app.Get("/health", func(ctx *fiber.Ctx) error {
        status := checker.Check()
        
        // Check if all services are healthy
        allHealthy := true
        for _, s := range status {
            if s != "healthy" {
                allHealthy = false
                break
            }
        }
        
        if allHealthy {
            return ctx.JSON(fiber.Map{"status": "healthy", "services": status})
        }
        
        return ctx.Status(503).JSON(fiber.Map{"status": "unhealthy", "services": status})
    })
}

完整示例

go
package main

import (
    "context"
    "os"
    "os/signal"
    "syscall"
    "time"
    
    "go.uber.org/fx"
)

func main() {
    app := fx.New(
        // Core modules
        fx.Provide(
            NewConfig,
            NewDatabase,
            NewRedis,
            NewCacheService,
            NewEventBus,
        ),
        
        // Business modules
        fx.Provide(
            NewUserModule,
            NewOrderModule,
        ),
        
        // HTTP server
        fx.Provide(NewHTTPServer),
        
        // Lifecycle hooks
        fx.Invoke(
            RegisterEventHandlers,
            RegisterHealthEndpoint,
            StartHTTPServer,
        ),
        
        // Shutdown timeout
        fx.StopTimeout(30*time.Second),
    )
    
    // Run application
    app.Run()
}

下一步

基于 Apache License 2.0 许可发布