Minit 是一个轻量级的 Go 应用程序初始化框架,提供模块化的启动流程管理、依赖注入和健康检查功能。
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.time 与 vcs.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 目录获取更多示例:
net/http 暴露 startup / readiness / liveness 探针入口控制优先使用 Run() / RunContext()
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 保持扁平
Provide / Resolve / ResolveAll / Has把 BuildInfo 当作“可覆盖默认值”
minit 自动读取 ReadBuildInfoWithBuildTime / WithGitCommitID状态读取走快照 API
AppInfo()ModuleStatus()HealthSnapshot()观测桥接走事件流
SubscribeLifecycleEvents详见 DESIGN.md。
详见 MIGRATION.md。
欢迎提交 Issue 和 Pull Request!
本项目采用 MIT 许可证 - 详见 LICENSE 文件。