package app_ser import ( "errors" "fmt" "github.com/urfave/cli/v2" "math/rand" "os" "os/signal" "project/config/logger" "runtime" "strings" "sync" "syscall" "time" ) const ( // Env EnvLocal = iota // 本地 EnvDebug // 测试 EnvProd // 线上 CliAppEnv = "app-env" CliAppLogMore = "app-log-more" ) var ( _EnvDevStr = "local" // 本地 _EnvTestStr = "debug" // 测试 _EnvProdStr = "release" // 线上 ) var ( _EnvMap = map[int][2]string{ EnvLocal: {_EnvDevStr, "本地环境"}, EnvDebug: {_EnvTestStr, "测试环境"}, EnvProd: {_EnvProdStr, "线上环境"}, } ) type _EnvFlag int func (v _EnvFlag) Usage() string { ret := make([]string, 0, len(_EnvMap)) for _, k := range []int{EnvLocal, EnvDebug, EnvProd} { s := _EnvMap[k] ret = append(ret, strings.Join(s[:], "-")) } return "env(" + strings.Join(ret, ", ") + ")" } func (v *_EnvFlag) Action(_ *cli.Context, sv string) error { match := false for ek, ev := range _EnvMap { if sv == ev[0] { *v, match = _EnvFlag(ek), true break } } if match != true { return errors.New("Env Is Invalid: " + sv) } return nil } type AppConfig struct { env _EnvFlag version string execDir string logMore bool } func (v *AppConfig) Env() int { return int(v.env) } func (v *AppConfig) EnvDesc() string { return _EnvMap[int(v.env)][0] } func (v *AppConfig) SetEnv(e int) bool { if _, ok := _EnvMap[int(v.env)]; ok { v.env = _EnvFlag(e) return true } return false } func (v *AppConfig) ExecDir() string { return v.execDir } func (v *AppConfig) Version() string { return v.version } func (v *AppConfig) LogMore() bool { return v.logMore } type Component interface { CliFlags() []cli.Flag Init(logger.Interface, *AppConfig, *cli.Context) error Run() error Close() error } type Service interface { CliFlags() []cli.Flag Init(*AppConfig, *cli.Context) (logger.Interface, error) Run(logger.Interface, *AppConfig) error Close() } type Application struct { Component map[string]Component Log logger.Interface Cfg AppConfig Ser Service } func (app *Application) closeComponent() { if app.Component != nil && len(app.Component) > 0 { wg := sync.WaitGroup{} wg.Add(len(app.Component)) for k := range app.Component { go func(v Component) { defer wg.Done() _ = v.Close() }(app.Component[k]) } wg.Wait() } } func (app *Application) shutdown() { defer func() { fmt.Println("Application Had Exit") os.Exit(0) }() fmt.Println("Application To Exit") } func (app *Application) catchSignal() { c := make(chan os.Signal) signal.Notify(c, syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM) for s := range c { switch s { case syscall.SIGHUP: fallthrough case syscall.SIGINT, syscall.SIGTERM: app.closeComponent() app.Ser.Close() app.shutdown() } } } func (app *Application) runComponent() error { if app.Component != nil && len(app.Component) > 0 { wg := sync.WaitGroup{} lock := sync.Mutex{} var errList []error wg.Add(len(app.Component)) for k := range app.Component { go func(v Component) { defer wg.Done() err := v.Run() if err != nil { lock.Lock() if errList == nil { errList = make([]error, 0) } errList = append(errList, err) lock.Unlock() } }(app.Component[k]) } wg.Wait() if len(errList) > 0 { errStr := make([]string, 0, len(errList)) for k := range errList { errStr = append(errStr, errList[k].Error()) } return errors.New("Component Run Error:" + strings.Join(errStr, ",")) } } return nil } func (app *Application) run() error { var err error = nil runtime.GOMAXPROCS(runtime.NumCPU()) go app.catchSignal() // 初始化随机种子 rand.Seed(time.Now().UnixNano()) // 启动服务 err = app.runComponent() if err != nil { return err } return app.Ser.Run(app.Log, &app.Cfg) } func (app *Application) Start(cmd *cli.App, args []string, service Service) { if cmd == nil { cmd = cli.NewApp() cmd.Version = "1.0" cmd.Name = "ApplicationWeb" cmd.Usage = "ApplicationWeb Server" } app.Ser = service // app cfs := make([][]cli.Flag, 0, len(app.Component)+2) cfs = append(cfs, []cli.Flag{ // base &cli.StringFlag{Name: CliAppEnv, Value: _EnvDevStr, Usage: app.Cfg.env.Usage(), Action: app.Cfg.env.Action}, &cli.BoolFlag{Name: CliAppLogMore, Value: false, Usage: "log more", Destination: &app.Cfg.logMore}, }) fsLen := len(cfs[0]) // service afs := app.Ser.CliFlags() if len(afs) > 0 { cfs = append(cfs, afs) fsLen += len(afs) } // component for k := range app.Component { v := app.Component[k].CliFlags() if len(v) > 0 { cfs = append(cfs, v) fsLen += len(v) } } // cli merge fs := make([]cli.Flag, 0, fsLen) for _, v := range cfs { fs = append(fs, v...) } // 执行命令行 cmd.Flags = fs cmd.Action = func(ctx *cli.Context) error { var err error = nil app.Cfg.execDir, err = os.Getwd() if err == nil { // 用命令行初始化配置 app.Log, err = app.Ser.Init(&app.Cfg, ctx) if err == nil { // 组件初始化 for k := range app.Component { if err = app.Component[k].Init(app.Log, &app.Cfg, ctx); err != nil { err = errors.New("Component Init Error: " + err.Error()) break } } // 启动程序 if err == nil { err = app.run() } } } else { err = errors.New("dir is invalid: " + app.Cfg.execDir) } return err } err := cmd.Run(args) if err != nil { fmt.Printf("Init Error: %s", err.Error()) os.Exit(1) } }