生命周期钩子
VEF Framework 基于 Uber FX 提供了应用生命周期管理。
生命周期阶段
- 构造阶段:创建和注入依赖
- 启动阶段:执行 OnStart 钩子
- 运行阶段:应用正常运行
- 停止阶段:执行 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()
}