288 lines
5.5 KiB
Go
288 lines
5.5 KiB
Go
package app_ser
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"gitea.party/public-messag-service/config/logger"
|
|
"github.com/urfave/cli/v2"
|
|
"math/rand"
|
|
"os"
|
|
"os/signal"
|
|
"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)
|
|
}
|
|
}
|