package main import ( "encoding/json" "flag" "fmt" "go_dreamfactory/comm" "go_dreamfactory/lego" "go_dreamfactory/lego/core" "go_dreamfactory/lego/sys/log" "io/ioutil" "net" "os" "os/exec" "path/filepath" "strconv" "strings" "time" "github.com/spf13/cobra" "gopkg.in/yaml.v3" ) /* 服务类型:区服启动程序 服务描述:通过读取游戏json配置,启动服务程序 */ var ( gmpath string //服务列表下标 crosspath string //服务列表下标 sid string //服务列表下标 onelog bool //日志路径 ) var confCmd = &cobra.Command{ Use: "conf", Short: "生成配置", Run: func(cmd *cobra.Command, args []string) { lego.Recover("conf") conf() }, } var startCmd = &cobra.Command{ Use: "start", Short: "启动程序", Run: func(cmd *cobra.Command, args []string) { lego.Recover("start") start() }, } var stopCmd = &cobra.Command{ Use: "stop", Short: "关闭程序", Run: func(cmd *cobra.Command, args []string) { lego.Recover("stop") stop() }, } var restart = &cobra.Command{ Use: "restart", Short: "重启服务", Run: func(cmd *cobra.Command, args []string) { lego.Recover("restart") stop() start() }, } func emptyRun(*cobra.Command, []string) {} var RootCmd = &cobra.Command{ Use: "dreamfactory", Short: "命令行", Long: "命令行工具", Run: emptyRun, } //初始化自定义cmd func init() { RootCmd.PersistentFlags().StringVarP(&gmpath, "gm", "g", "./gm.json", "游戏区服配置") RootCmd.PersistentFlags().StringVarP(&crosspath, "cross", "c", "./cross.json", "游戏跨服配置") RootCmd.PersistentFlags().StringVarP(&sid, "sid", "i", "", "区服id") RootCmd.PersistentFlags().BoolVarP(&onelog, "log", "l", false, "输出日志") RootCmd.AddCommand(confCmd, startCmd, stopCmd, restart) } func main() { flag.Parse() if err := log.OnInit(nil, log.SetFileName("./s.log"), log.SetLoglevel(log.DebugLevel), log.SetUniqueLog(true), log.SetIsDebug(false)); err != nil { panic(fmt.Sprintf("Sys log Init err:%v", err)) } else { log.Infof("Sys log Init success !") } Execute() } //执行命令 func Execute() { if err := RootCmd.Execute(); err != nil { log.Errorln(err) os.Exit(1) } } //生成配置 func conf() { if config, err := readergmconf(gmpath); err != nil { log.Error("读取区服配置失败!", log.Field{Key: "err", Value: err.Error()}) return } else { if ss, err := rederServiceSttings(config); err != nil { log.Error("转换服务配置异常!", log.Field{Key: "err", Value: err.Error()}) return } else { for _, v := range ss { if sid == "" || fmt.Sprintf("%s_%s", v.Tag, sid) == v.Id { if err = writeServiceConfig(fmt.Sprintf("./conf/%s.yaml", v.Id), v); err != nil { log.Error("写入配置文件失败!", log.Field{Key: "err", Value: err.Error()}) return } } } } } log.Debugf("conf succ!") } //启动程序 func start() { if config, err := readergmconf(gmpath); err != nil { log.Error("读取区服配置失败!", log.Field{Key: "err", Value: err.Error()}) } else { var ( maintes *core.ServiceSttings workers []*core.ServiceSttings = make([]*core.ServiceSttings, 0) gateways []*core.ServiceSttings = make([]*core.ServiceSttings, 0) ) if ss, err := rederServiceSttings(config); err != nil { log.Error("转换服务配置异常!", log.Field{Key: "err", Value: err.Error()}) } else { for _, v := range ss { if sid == "" || fmt.Sprintf("%s_%s", v.Tag, sid) == v.Id { if err = writeServiceConfig(fmt.Sprintf("./conf/%s.yaml", v.Id), v); err != nil { log.Error("写入配置文件失败!", log.Field{Key: "err", Value: err.Error()}) return } switch v.Type { case comm.Service_Gateway: //网关服务 gateways = append(gateways, v) break case comm.Service_Worker: //业务服务 workers = append(workers, v) break case comm.Service_Mainte: //维护服务 maintes = v break default: err = fmt.Errorf("服务类型异常 stype:%s", v.Type) return } } } } //优先启动 维护服 if maintes != nil { if err = startService(maintes); err != nil { log.Error("启动服务失败!", log.Field{Key: "id", Value: maintes.Id}, log.Field{Key: "err", Value: err.Error()}, ) return } } time.Sleep(time.Second * 3) // 业务服 for _, v := range workers { if err = startService(v); err != nil { log.Error("启动服务失败!", log.Field{Key: "id", Value: v.Id}, log.Field{Key: "err", Value: err.Error()}, ) return } } time.Sleep(time.Second * 3) // 网关服 for _, v := range gateways { if err = startService(v); err != nil { log.Error("启动服务失败!", log.Field{Key: "id", Value: v.Id}, log.Field{Key: "err", Value: err.Error()}, ) return } } } log.Errorf("start succ!") } //关闭程序 func stop() { if config, err := readergmconf(gmpath); err != nil { log.Error("读取区服配置失败!", log.Field{Key: "err", Value: err.Error()}) } else { if ss, err := rederServiceSttings(config); err != nil { log.Error("转换服务配置异常!", log.Field{Key: "err", Value: err.Error()}) } else { for _, v := range ss { if sid == "" || fmt.Sprintf("%s_%s", v.Tag, sid) == v.Id { stopService(v) } } } } log.Infof("stop succ!") } ///转换区服配置到服务配置 func rederServiceSttings(config *comm.GameConfig) (ss []*core.ServiceSttings, err error) { ss = make([]*core.ServiceSttings, 0) var ( ip string port int sseting *core.ServiceSttings ) if config.Mainte != "" { if ip, port, err = parseaddr(config.Mainte); err != nil { return } else { if sseting, err = convertServiceSttings(config, 0, comm.Service_Mainte, ip, config.MaintePort, port, config.OpenServiceTime); err != nil { return } ss = append(ss, sseting) } } for i, v := range config.Workers { if ip, port, err = parseaddr(v); err != nil { return } else { if sseting, err = convertServiceSttings(config, i, comm.Service_Worker, ip, port, 0, config.OpenServiceTime); err != nil { return } ss = append(ss, sseting) } } for i, v := range config.Gateways { if ip, port, err = parseaddr(v); err != nil { return } else { if sseting, err = convertServiceSttings(config, i, comm.Service_Gateway, ip, config.GatewayPorts[i], port, config.OpenServiceTime); err != nil { return } ss = append(ss, sseting) } } return } //读取游戏配置文件 func readergmconf(path string) (config *comm.GameConfig, err error) { config = &comm.GameConfig{} var ( jsonFile *os.File byteValue []byte ) if jsonFile, err = os.Open(path); err != nil { return } else { defer jsonFile.Close() if byteValue, err = ioutil.ReadAll(jsonFile); err != nil { return } err = json.Unmarshal(byteValue, config) } return } //转换游戏服务配置 func convertServiceSttings(config *comm.GameConfig, id int, stype string, ip string, rport int, lport int, opentime string) (sseting *core.ServiceSttings, err error) { sseting = &core.ServiceSttings{} sseting.Tag = config.AreaId sseting.Ip = ip sseting.Port = rport sseting.Opentime = opentime sseting.Modules = make(map[string]map[string]interface{}) sseting.Sys = make(map[string]map[string]interface{}) sseting.Sys["rpcx"] = map[string]interface{}{ "ConsulServers": config.ConsulAddr, } switch stype { case comm.Service_Gateway: //网关服务 sseting.Id = fmt.Sprintf("%s_%s%d", config.AreaId, comm.Service_Gateway, id) sseting.Type = comm.Service_Gateway sseting.Sys["rpcx"]["RpcxStartType"] = 1 sseting.Modules["gateway"] = map[string]interface{}{ "ListenPort": lport, } break case comm.Service_Worker: //业务服务 sseting.Id = fmt.Sprintf("%s_%s%d", config.AreaId, comm.Service_Worker, id) sseting.Type = comm.Service_Worker sseting.Sys["rpcx"]["RpcxStartType"] = 2 if config.BattleAddr != "" { sseting.Modules["battle"] = map[string]interface{}{ "OpenCheck": true, "BattleServerAddr": config.BattleAddr, } } else { sseting.Modules["battle"] = map[string]interface{}{ "OpenCheck": false, } } break case comm.Service_Mainte: //维护服务 sseting.Id = fmt.Sprintf("%s_%s", config.AreaId, comm.Service_Mainte) sseting.Type = comm.Service_Mainte sseting.Sys["rpcx"]["RpcxStartType"] = 2 sseting.Modules["web"] = map[string]interface{}{ "WebDir": "./dist", "Port": lport, "Key": "@234%67g12q4*67m12#4l67!", } break default: err = fmt.Errorf("服务类型异常 stype:%s", sseting.Type) return } if config.OpenGM { sseting.Modules["chat"] = map[string]interface{}{ "GM": true, } } if !onelog { sseting.Sys["rpcx"]["Debug"] = true sseting.Sys["log"] = map[string]interface{}{ "FileName": fmt.Sprintf("./log/%s.log", sseting.Id), "IsDebug": true, "Loglevel": log.DebugLevel, "MaxAgeTime": 7, } } else { sseting.Sys["log"] = map[string]interface{}{ "Alias": sseting.Id, "FileName": "./s.log", "IsDebug": false, "Loglevel": log.ErrorLevel, "MaxAgeTime": 7, "UniqueLog": true, } } sseting.Sys["configure"] = map[string]interface{}{ "ConfigurePath": "./json", "TimestampFile": fmt.Sprintf("/home/www/default/changetime/_data_dreamworksserver_s%s.txt", config.AreaId), } sseting.Sys["wordfilter"] = map[string]interface{}{ "WorldFile": "./wordfilter.json", } sseting.Sys["db"] = map[string]interface{}{ "IsCross": config.IsCross, "CrossChannel": config.BelongCrossServerId, "RedisIsCluster": config.LoaclDB.RedisIsCluster, "RedisAddr": config.LoaclDB.RedisAddr, "RedisPassword": config.LoaclDB.RedisPassword, "RedisDB": config.LoaclDB.RedisDB, "MongodbUrl": config.LoaclDB.MongodbUrl, "MongodbDatabase": config.LoaclDB.MongodbDatabase, "CrossConfig": crosspath, } return } //启动服务程序 func startService(sseting *core.ServiceSttings) (err error) { var ( cmd *exec.Cmd command string confpath string = fmt.Sprintf("./conf/%s.yaml", sseting.Id) output []byte ) switch sseting.Type { case comm.Service_Gateway: //网关服务 command = fmt.Sprintf("./stup.sh start %s gateway %s", sseting.Id, confpath) break case comm.Service_Worker: //业务服务 command = fmt.Sprintf("./stup.sh start %s worker %s", sseting.Id, confpath) break case comm.Service_Mainte: //维护服务 command = fmt.Sprintf("./stup.sh start %s mainte %s", sseting.Id, confpath) break default: err = fmt.Errorf("服务类型异常 stype:%s", sseting.Type) return } log.Debug("启动外部命令", log.Field{Key: "cmd", Value: command}) cmd = exec.Command("/bin/bash", "-c", command) if output, err = cmd.CombinedOutput(); err != nil { return } log.Debugln(string(output)) return } //启动服务程序 func stopService(sseting *core.ServiceSttings) (err error) { var ( cmd *exec.Cmd command string output []byte ) switch sseting.Type { case comm.Service_Gateway: //网关服务 command = fmt.Sprintf("./stup.sh stop %s ", sseting.Id) break case comm.Service_Worker: //业务服务 command = fmt.Sprintf("./stup.sh stop %s", sseting.Id) break case comm.Service_Mainte: //维护服务 command = fmt.Sprintf("./stup.sh stop %s ", sseting.Id) break default: err = fmt.Errorf("服务类型异常 stype:%s", sseting.Type) return } log.Debug("启动外部命令", log.Field{Key: "cmd", Value: command}) cmd = exec.Command("/bin/bash", "-c", command) if output, err = cmd.CombinedOutput(); err != nil { return } log.Debugln(string(output)) return } //写入服务配置文件 func writeServiceConfig(filename string, sseting *core.ServiceSttings) (err error) { var data []byte if err = os.MkdirAll(filepath.Dir(filename), 0755); err != nil { return } if data, err = yaml.Marshal(sseting); err != nil { return } else { err = ioutil.WriteFile(filename, data, 0777) } return } func parseaddr(addr string) (ip string, port int, err error) { ss := strings.Split(addr, ":") if len(ss) != 2 { err = fmt.Errorf("addr:%s解析异常", addr) return } address := net.ParseIP(ss[0]) if address == nil { err = fmt.Errorf("addr:%s解析异常 ip:%s 校验失败", addr, ss[0]) return } ip = ss[0] if port, err = strconv.Atoi(ss[1]); err != nil { err = fmt.Errorf("addr:%s解析异常 port:%s 校验失败", addr, ss[1]) return } return }