475 lines
12 KiB
Go
475 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 //服务列表下标
|
|
openlog 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(&openlog, "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": config.BattleOpen,
|
|
"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 openlog {
|
|
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": fmt.Sprintf("./log/%s.log", sseting.Id),
|
|
"IsDebug": false,
|
|
"Loglevel": log.InfoLevel,
|
|
"MaxAgeTime": 7,
|
|
}
|
|
}
|
|
|
|
sseting.Sys["configure"] = map[string]interface{}{
|
|
"ConfigurePath": "./json",
|
|
"TimestampFile": "./timestamp.text",
|
|
}
|
|
sseting.Sys["wordfilter"] = map[string]interface{}{
|
|
"WorldFile": []string{"./wordfilter.json", "./wordfilter.txt"},
|
|
}
|
|
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
|
|
}
|