package main import ( "os" "os/signal" "runtime" "syscall" "time" "amuz.es/gogs/infra/rooibos/subsys/http" "amuz.es/gogs/infra/rooibos/subsys/periodic" "amuz.es/gogs/infra/rooibos/util" "gopkg.in/alecthomas/kingpin.v2" "sync" "context" ) var ( app = kingpin.New(util.Config.Name(), "graceful API server").Author("tom.cat") verbose = app.Flag("verbose", "Enable verbose mode.").Short('v').Bool() logDir = app.Flag("log-dir", "logstore directory.").Short('L').String() configFile = app.Flag("config-file", "config file path").Default("settings.yml").HintOptions("FILENAME").Short('C').String() logger = util.NewLogger("main") ) func systemStart(ctx context.Context, errorSignal chan error, waiter *sync.WaitGroup) { defer func() { if err := recover(); err != nil { errorSignal <- err.(error) } }() start := time.Now() app.Version(util.Config.Version()) if _, err := app.Parse(os.Args[1:]); err != nil { panic(err) } setMaxProcs() if err := util.LoadConfig(*configFile); err != nil { panic(err) } util.InitLogger(*verbose, *logDir, &util.Config.Logging.Application) showBanner(util.Config.Phase, util.Config.Version(), util.Config.BuildDate()) // init subsystem //db.InitDB(&util.Config.Db) //redis.InitRedis(&util.Config.Redis) //redis.AppTokenInitService(util.Config.Phase, util.Config.AppToken.Expires, util.Config.AppToken.RefreshExpires) //redis.UploadFileInitService(util.Config.Phase, util.Config.AppToken.UploadRetryExpires) periodic.Start(errorSignal, ctx, waiter) http.Start(util.Config.Bind, &util.Config.Logging.Access, errorSignal, ctx, waiter) logger.Info("bootstrapped application ", time.Since(start)) util.NotifyDaemon(util.DaemonStarted) } func systemReload() { util.RotateLogger() } func systemTeardown(exitCode *int, waiter *sync.WaitGroup) { logger.Info("closing application") util.NotifyDaemon(util.DaemonStopping) waiter.Wait() //redis.CloseRedis() logger.Info("bye") os.Exit(*exitCode) } func main() { var ( exitCode = 0 ctx, canceled = context.WithCancel(context.Background()) waiter = &sync.WaitGroup{} exitSignal = make(chan os.Signal, 1) errorSignal = make(chan error, 10) ) systemStart(ctx, errorSignal, waiter) defer systemTeardown(&exitCode, waiter) signal.Notify(exitSignal, syscall.SIGINT, syscall.SIGTERM, syscall.SIGHUP) for { select { case <-ctx.Done(): logger.Info("Service request to close this application") return case sysSignal := <-exitSignal: switch sysSignal { case syscall.SIGHUP: systemReload() default: canceled() logger.Info("SYSCALL! ", sysSignal.String()) return } case err := <-errorSignal: canceled() logger.Error("exception raised! ", err) exitCode = -1 return } } } func setMaxProcs() { // TODO(vmarmol): Consider limiting if we have a CPU mask in effect. // Allow as many threads as we have cores unless the user specified a value. var numProcs int // if *maxProcs < 1 { numProcs = runtime.NumCPU() // } else { // numProcs = *maxProcs // } runtime.GOMAXPROCS(numProcs) // Check if the setting was successful. actualNumProcs := runtime.GOMAXPROCS(0) if actualNumProcs != numProcs { logger.Warn("Specified max procs of %v but using %v", numProcs, actualNumProcs) } } func showBanner(phase string, version string, buildDate string) { if util.LoggerIsStd() { logger.Info(` { { } }_{ __{ .-{ } }-. ` + util.Config.Name() + ` ( } { ) version: ` + version + ` ` + "|`-.._____..-'| buildDate: " + buildDate + ` | ;--. phase: ` + phase + ` | (__ \ | | ) ) | |/ / | / / | ( / \ y' ` + "`-.._____..-'") } else { logger. WithField("version", version). WithField("buildDate", buildDate). WithField("phase", phase). Info("##", util.Config.Name()) } }