logo
0
0
WeChat Login

Minit

Go Reference Go Report Card MIT License

Minit 是一个轻量级的 Go 应用程序初始化框架,提供模块化的启动流程管理、依赖注入和健康检查功能。

目录

特性

  • 🚀 模块化的应用程序初始化流程
  • 🔄 自动的依赖关系管理
  • 🎯 支持模块生命周期钩子
  • 💉 内置依赖注入容器
  • ❤️ 健康检查支持(startup / readiness / liveness)
  • 🧭 支持 Context 化运行、等待与优雅关闭
  • 📡 提供生命周期事件流与运行时状态快照
  • 🎨 美观的启动界面和日志输出

安装

go get github.com/darkit/minit

快速开始

package main import ( "github.com/darkit/minit" ) func main() { app := minit.NewApp[AppModule]("MyApp"). WithVersion("1.0.0"). WithLogo(` ██████╗ ██████╗ ██╔══██╗██╔═══██╗ ██████╔╝██║ ██║ ██╔══██╗██║ ██║ ██║ ██║╚██████╔╝ ╚═╝ ╚═╝ ╚═════╝ `). WithLogoAlignCenter(). // 可选:居中显示 Logo;默认左对齐 EnableHealthCheck() if err := app.Build(); err != nil { panic(err) } <-minit.Wait() }

核心功能

模块生命周期

package main import ( "github.com/darkit/minit/module" ) // 生命周期接口 type LifecycleModule interface { module.Module Start() error // 启动模块 Stop() error // 停止模块 IsRunning() bool // 检查运行状态 } type StartContextModule interface { module.LifecycleModule StartContext(ctx context.Context) error } type StopContextModule interface { module.LifecycleModule StopContext(ctx context.Context) error } // 基础生命周期模块 type BaseLifecycleModule struct { running atomic.Bool } // 示例模块 type AppModule struct { BaseLifecycleModule } func (m *AppModule) DependsModule() []module.Module { return []module.Module{ &DatabaseModule{}, &CacheModule{}, } }

依赖注入

package main import "github.com/darkit/minit" // 注册服务 minit.Provide[IDatabase](NewDatabase()) minit.Provide[IDatabase](NewDatabase(), minit.WithServiceName("main")) // 获取服务实例 db, _ := minit.Resolve[IDatabase]() mainDB, _ := minit.Resolve[IDatabase]("main") // 获取所有服务 services := minit.ResolveAll[IDatabase]() for _, service := range services { fmt.Println(service.Name, service.Service) } // 检查服务是否存在 if minit.Has[IDatabase]() { // 使用数据库服务 } // 清理服务 minit.Clear[IDatabase]()

模块依赖管理

// 声明依赖 func (m AppModule) DependsModule() []module.Module { return []module.Module{ &DatabaseModule{}, &CacheModule{}, } }

特点:

  • 自动依赖注入
  • 依赖顺序管理
  • 循环依赖检测

健康检查

package main import ( "log" "time" "github.com/darkit/minit" "github.com/darkit/minit/health" ) // 方式一:实现接口 type AppHealthCheck struct{} func (h *AppHealthCheck) Check() (string, error) { return "应用服务", nil } func (h *AppHealthCheck) Timeout() time.Duration { return 5 * time.Second } // 注册为 readiness 健康检查 minit.RegisterHealthCheck(&AppHealthCheck{}, health.WithKind(health.KindReadiness)) // startup 检查:Build() 阶段会自动执行 minit.AddHealthCheck("数据库启动检查", func() error { // 检查数据库连接 return nil }) // readiness 检查:通常由 HTTP / gRPC probe 主动触发 minit.AddReadinessCheck("缓存就绪", func() error { return nil }) // liveness 检查:可用于保活自检 minit.AddLivenessCheck("Worker 存活", func() error { return nil }) // 主动执行 readiness 检查 if err := minit.RunReadinessChecks(); err != nil { log.Printf("readiness 失败: %v", err) } // 查看最近一次健康检查快照 snapshots, _ := minit.HealthSnapshot(minit.HealthKindReadiness) for _, item := range snapshots { log.Printf("%s healthy=%v err=%s", item.Name, item.Healthy, item.LastError) } // 内置 net/http handler,适合直接挂到标准库 HTTP 服务 mux := http.NewServeMux() mux.Handle("/healthz/startup", minit.StartupHandler()) mux.Handle("/healthz/readiness", minit.ReadinessHandler()) mux.Handle("/healthz/liveness", minit.LivenessHandler()) // drain / cutover 场景:手动关闭 readiness _ = minit.MarkNotReady("draining") defer minit.MarkReady()

优雅关闭

import ( "context" "os" "os/signal" "syscall" ) func main() { app := minit.NewApp[AppModule]("MyApp"). WithVersion("1.0.0"). EnableHealthCheck() if err := app.Build(); err != nil { panic(err) } // 新方式:让 minit 接管等待和优雅关闭 ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM) defer stop() if err := minit.RunContext(ctx); err != nil { panic(err) } // 兼容旧方式: // <-minit.Wait() // minit.Exit(0) }

若模块本身需要感知 shutdown timeout / cancel,可实现 StopContext(ctx)

type HTTPModule struct { module.BaseLifecycleModule server *http.Server } func (m *HTTPModule) DependsModule() []module.Module { return nil } func (m *HTTPModule) StartContext(ctx context.Context) error { _ = ctx return m.BaseLifecycleModule.Start() } func (m *HTTPModule) StopContext(ctx context.Context) error { defer m.BaseLifecycleModule.Stop() return m.server.Shutdown(ctx) }

日志定制

Logo 默认左对齐;若需要居中渲染可调用:

app := minit.NewApp[AppModule]("MyApp"). WithLogo(logo). WithLogoAlignCenter()

若未显式调用 WithBuildTime() / WithGitCommitID()minit 会尝试从 runtime/debug.ReadBuildInfo() 自动回填 vcs.timevcs.revision

package main import "github.com/darkit/minit" // 设置日志级别 minit.SetLogLevel(minit.LevelDebug) // 调试级别 minit.SetLogLevel(minit.LevelInfo) // 信息级别 minit.SetLogLevel(minit.LevelWarn) // 警告级别 minit.SetLogLevel(minit.LevelError) // 错误级别 // 自定义日志实现 type CustomLogger struct{} func (l *CustomLogger) Infof(format string, args ...interface{}) { /* ... */ } func (l *CustomLogger) Warnf(format string, args ...interface{}) { /* ... */ } func (l *CustomLogger) Debugf(format string, args ...interface{}) { /* ... */ } func (l *CustomLogger) Errorf(format string, args ...interface{}) { /* ... */ } // 可选:若希望 minit.SetLogLevel 生效,再实现 LevelSetter func (l *CustomLogger) SetLevel(level minit.Level) { /* ... */ } // 设置默认日志实现 minit.SetLogger(&CustomLogger{}) // 或者只为当前应用设置专用 logger app := minit.NewApp[AppModule]("MyApp"). WithLogger(&CustomLogger{})

生命周期事件与状态快照

unsubscribe := minit.SubscribeLifecycleEvents(func(event minit.LifecycleEvent) { fmt.Printf("event=%s phase=%s module=%s err=%v\n", event.Type, event.Phase, event.Module, event.Error) }) defer unsubscribe() info, _ := minit.AppInfo() modules, _ := minit.ModuleStatus() checks, _ := minit.HealthSnapshot() snapshot, _ := minit.RuntimeSnapshot() fmt.Println(info.AppName, len(modules), len(checks)) fmt.Println(snapshot.AppInfo.Ready) _ = minit.WriteRuntimeJSON(os.Stdout) mux := http.NewServeMux() mux.Handle("/debug/runtime", minit.RuntimeJSONHandler()) // 仅允许 GET / HEAD

高级特性

模块缓存

module1, err := minit.GetModule[*DatabaseModule]() if err != nil { panic(err) } module2, err := minit.GetModule[*DatabaseModule]() if err != nil { panic(err) } // module1 == module2

条件初始化

func (m *ConfigModule) Initialize() { if !m.initialized { // 执行初始化 m.initialized = true } }

自定义初始化顺序

func (m PriorityModule) Priority() int { return 100 // 数字越小优先级越高 }

并行初始化

func (m AppModule) InitializeConcurrent() bool { return true }

错误处理

// 初始化错误 if err := app.Build(); err != nil { log.Fatalf("应用启动失败: %v", err) } // 健康检查错误 minit.AddHealthCheck("数据库", func() error { return fmt.Errorf("数据库检查失败") }) // 生命周期错误 type ErrorModule struct { module.BaseLifecycleModule } func (m *ErrorModule) Start() error { return fmt.Errorf("启动失败") }

调试支持

// 查看依赖图 graph, err := app.GetModuleGraph() if err != nil { panic(err) } for module, deps := range graph { fmt.Printf("%s 依赖: %v\n", module, deps) }

性能优化

  • 保持模块粒度适中
  • 避免过深的依赖链
  • 合理使用延迟初始化
  • 利用并行初始化
  • 使用模块缓存

示例

查看 examples 目录获取更多示例:

最佳实践

  • 入口控制优先使用 Run() / RunContext()

    • 普通服务、daemon、后台进程优先用 app.Run()app.RunContext(ctx)
    • 只有在你必须手写退出编排时,才回退到 Build() + Wait() / Shutdown() / Exit()
  • 把健康检查按职责拆开

    • startup:只放启动期必须成功的依赖
    • readiness:放“是否可以接流量”
    • liveness:放“进程是否还活着且无需重启”
    • 需要优雅摘流时,用 MarkNotReady("draining") 先关 readiness,再走 Shutdown()
    • 不要把慢查询、全量外部巡检塞进 liveness
  • 模块只做自己的生命周期

    • PreInitialize:准备全局前置条件
    • Initialize:构造依赖、注册服务、注册健康检查
    • Start:启动监听、worker、后台协程
    • Shutdown:释放资源
    • 需要感知超时与取消时,优先实现 StartContext(ctx) / StopContext(ctx)
    • 避免在 DependsModule() 里做副作用
  • DI 保持扁平

    • 对外优先使用根包 facade:Provide / Resolve / ResolveAll / Has
    • 服务命名只在确实存在多实例时使用
    • 不要把瞬时业务参数塞进全局 DI
  • 把 BuildInfo 当作“可覆盖默认值”

    • 默认让 minit 自动读取 ReadBuildInfo
    • 只有 CI 需要强控格式时,再显式调用 WithBuildTime / WithGitCommitID
  • 状态读取走快照 API

    • 管理端、探针端点、调试接口优先使用:
      • AppInfo()
      • ModuleStatus()
      • HealthSnapshot()
    • 不要直接窥探内部状态对象
  • 观测桥接走事件流

    • 需要接入 metrics / tracing / 审计时,优先订阅 SubscribeLifecycleEvents
    • 不要把具体观测 SDK 反向耦合进启动内核

设计说明

详见 DESIGN.md

迁移指南

详见 MIGRATION.md

贡献

欢迎提交 Issue 和 Pull Request!

许可证

本项目采用 MIT 许可证 - 详见 LICENSE 文件。

About

一个轻量级的 Go 应用程序初始化框架,提供模块化的启动流程管理、依赖注入和健康检查功能。

81.31 MiB
0 forks0 stars3 branches0 TagREADMEMIT license
Language
Go100%