go_dreamfactory/services/cmd/main.go
2023-11-29 09:54:59 +08:00

584 lines
18 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"
"golang.org/x/crypto/ssh"
"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()
},
}
var sync = &cobra.Command{
Use: "sync",
Short: "同步服务器",
Run: func(cmd *cobra.Command, args []string) {
lego.Recover("sync")
syncServer()
},
}
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, sync)
}
func main() {
flag.Parse()
if err := log.OnInit(nil,
log.SetFileName("./s.log"),
log.SetLoglevel(log.DebugLevel),
log.SetUniqueLog(true),
log.SetIsDebug(true)); 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 syncServer() {
if sid != "" {
switch sid {
case "qa":
exesshcomd("10.0.0.9", `
curl -XPOST -s -L 'https://oapi.dingtalk.com/robot/send?access_token=c6d2066cd4b36882b5dc3033e359a1c1b259eb4fd6cb69f397a65f544dbce86f' -H 'Content-Type: application/json' -H "charset:utf-8" -d '{"msgtype": "text","text": {"content": "* 服务准备同步--QA测试服"}}';
cd /home/liwei/dreamworks; svn revert -R . ; svn update
sudo cp -f /home/liwei/go_dreamfactory/bin/cmd /home/liwei/dreamworks/cmd;
sudo cp -f /home/liwei/go_dreamfactory/bin/gateway /home/liwei/dreamworks/gateway;
sudo cp -f /home/liwei/go_dreamfactory/bin/mainte /home/liwei/dreamworks/mainte;
sudo cp -f /home/liwei/go_dreamfactory/bin/worker /home/liwei/dreamworks/worker;
sudo cp -f /home/liwei/go_dreamfactory/bin/json/* /home/liwei/dreamworks/json/;
sudo cp -f /home/liwei/go_dreamfactory/bin/wordfilter.txt /home/liwei/dreamworks/wordfilter.txt;
cd /home/liwei/dreamworks; svn add . --no-ignore --force ; svn commit -m "同步服务器" *;
curl -XPOST -s -L 'https://oapi.dingtalk.com/robot/send?access_token=c6d2066cd4b36882b5dc3033e359a1c1b259eb4fd6cb69f397a65f544dbce86f' -H 'Content-Type: application/json' -H "charset:utf-8" -d '{"msgtype": "text","text": {"content": "* 服务停止--QA测试服"}}';
`)
exesshcomd("101.35.121.71", `
cd /data/dreamworksserver/s80; python stopserver.py; python install.py; python start.py;
cd /data/dreamworksserver/s100; python stopserver.py; python install.py; python start.py;
`)
exesshcomd("101.35.125.220", `
cd /data/dreamworksserver/s90; python stopserver.py; python install.py; python start.py;
cd /data/dreamworksserver/s110; python stopserver.py; python install.py; python start.py;
curl -XPOST -s -L 'https://oapi.dingtalk.com/robot/send?access_token=c6d2066cd4b36882b5dc3033e359a1c1b259eb4fd6cb69f397a65f544dbce86f' -H 'Content-Type: application/json' -H "charset:utf-8" -d '{"msgtype": "text","text": {"content": "* 服务启动--QA测试服"}}';
`)
case "dw":
exesshcomd("10.0.0.9", `
curl -XPOST -s -L 'https://oapi.dingtalk.com/robot/send?access_token=c6d2066cd4b36882b5dc3033e359a1c1b259eb4fd6cb69f397a65f544dbce86f' -H 'Content-Type: application/json' -H "charset:utf-8" -d '{"msgtype": "text","text": {"content": "* 服务准备同步--DW测试服"}}';
cd /home/liwei/dreamworks; svn revert -R . ; svn update
sudo cp -f /home/liwei/go_dreamfactory/bin/cmd /home/liwei/dreamworks/cmd;
sudo cp -f /home/liwei/go_dreamfactory/bin/gateway /home/liwei/dreamworks/gateway;
sudo cp -f /home/liwei/go_dreamfactory/bin/mainte /home/liwei/dreamworks/mainte;
sudo cp -f /home/liwei/go_dreamfactory/bin/worker /home/liwei/dreamworks/worker;
sudo cp -f /home/liwei/go_dreamfactory/bin/json/* /home/liwei/dreamworks/json/;
sudo cp -f /home/liwei/go_dreamfactory/bin/wordfilter.txt /home/liwei/dreamworks/wordfilter.txt;
cd /home/liwei/dreamworks; svn add . --no-ignore --force ; svn commit -m "同步服务器" *;
curl -XPOST -s -L 'https://oapi.dingtalk.com/robot/send?access_token=c6d2066cd4b36882b5dc3033e359a1c1b259eb4fd6cb69f397a65f544dbce86f' -H 'Content-Type: application/json' -H "charset:utf-8" -d '{"msgtype": "text","text": {"content": "* 服务停止--DW测试服"}}';
`)
exesshcomd("101.35.121.71", `
cd /data/dreamworksserver/s40; python stopserver.py; python install.py; python start.py;
`)
exesshcomd("101.35.125.220", `
cd /data/dreamworksserver/s50; python stopserver.py; python install.py; python start.py;
curl -XPOST -s -L 'https://oapi.dingtalk.com/robot/send?access_token=c6d2066cd4b36882b5dc3033e359a1c1b259eb4fd6cb69f397a65f544dbce86f' -H 'Content-Type: application/json' -H "charset:utf-8" -d '{"msgtype": "text","text": {"content": "* 服务启动--DW测试服"}}';
`)
case "battle":
exesshcomd("10.0.0.9", `
cd /home/liwei/dfbattle/output; ./stop.sh;
cd /home/liwei/fightdll; svn update;
sudo cp -f /home/liwei/fightdll/FightRunner.dll /home/liwei/dfbattle/lib/FightRunner.dll;
sudo cp -f /home/liwei/fightdll/GameFight.dll /home/liwei/dfbattle/lib/GameFight.dll;
sudo cp -f /home/liwei/fightdll/GameProto.dll /home/liwei/dfbattle/lib/GameProto.dll;
sudo cp -r -f /home/liwei/fightdll/GameConfig/* /home/liwei/dfbattle/GameConfig/;
cd /home/liwei/dfbattle; dotnet clean; dotnet build -o output;
sudo cp -r -f /home/liwei/dfbattle/GameConfig/* /home/liwei/dfbattle/output/GameConfig/;
cd /home/liwei/dfbattle/output; ./start.sh ;
cd /home/liwei/dfbattle; git add ./lib/* ./GameConfig/*; git commit -m 同步战斗服; git push;
`)
}
}
}
func exesshcomd(addr, cmd string) (err error) {
var (
config *ssh.ClientConfig
sshClient *ssh.Client
session *ssh.Session
combo []byte
)
//创建sshp登陆配置
config = &ssh.ClientConfig{
Timeout: time.Second, //ssh 连接time out 时间一秒钟, 如果ssh验证错误 会在一秒内返回
User: "root",
HostKeyCallback: ssh.InsecureIgnoreHostKey(), //这个可以, 但是不够安全
}
config.Auth = []ssh.AuthMethod{ssh.Password("Legu.cc()123")}
//dial 获取ssh client
sshClient, err = ssh.Dial("tcp", fmt.Sprintf("%s:22", addr), config)
if err != nil {
log.Fatal("创建ssh client 失败", log.Field{Key: "err", Value: err.Error()})
}
defer sshClient.Close()
//创建ssh-session
session, err = sshClient.NewSession()
if err != nil {
log.Fatal("创建ssh session 失败", log.Field{Key: "err", Value: err.Error()})
}
defer session.Close()
//执行远程命令
combo, err = session.CombinedOutput(cmd)
if err != nil {
log.Fatal("远程执行cmd 失败", log.Field{Key: "err", Value: err.Error()})
}
log.Println("命令输出:", string(combo))
return
}
// /转换区服配置到服务配置
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
}