go_dreamfactory/services/cmd/main.go
2023-06-16 15:15:13 +08:00

476 lines
12 KiB
Go

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!",
"Debug": true,
}
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": "./timestamp.text",
}
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
}