410 lines
10 KiB
Go
410 lines
10 KiB
Go
package main
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"log"
|
|
"os"
|
|
"os/exec"
|
|
"path/filepath"
|
|
"strconv"
|
|
"strings"
|
|
"syscall"
|
|
"time"
|
|
|
|
"gopkg.in/ini.v1"
|
|
)
|
|
|
|
// GOOS=linux GOARCH=amd64 go build -o /data/devops/Goleaf/tool/tool main.go
|
|
var help = `
|
|
Usage: app.ini [options]
|
|
|
|
start start the server
|
|
stop stop the server
|
|
restart restart the server
|
|
status get the server status
|
|
|
|
`
|
|
var cfg *ini.File
|
|
var FuncMap map[string]func() error
|
|
var dirPath string
|
|
var app_path string
|
|
|
|
func main() {
|
|
FuncMap = make(map[string]func() error)
|
|
// 检查是否提供了命令行参数
|
|
if len(os.Args) < 2 {
|
|
log.Fatal(help)
|
|
}
|
|
var err error
|
|
|
|
// 获取当前文件的绝对路径
|
|
execPath, err := os.Executable()
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
absPath, err := filepath.Abs(execPath)
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
// 获取当前文件的绝对路径的文件夹路径
|
|
dirPath = filepath.Dir(absPath)
|
|
|
|
// 加载 app.ini 文件
|
|
cfg, err = ini.Load(dirPath + "/app.ini")
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
app_path = cfg.Section("app").Key("app_path").String()
|
|
register("start", start)
|
|
register("stop", stop)
|
|
register("install", install)
|
|
register("status", status)
|
|
register("restart", restart)
|
|
funcName := os.Args[1]
|
|
if f, ok := FuncMap[funcName]; ok {
|
|
e := f()
|
|
if e != nil {
|
|
log.Fatal(e)
|
|
}
|
|
} else {
|
|
log.Fatal(help)
|
|
}
|
|
}
|
|
|
|
func register(name string, f func() error) {
|
|
FuncMap[name] = f
|
|
}
|
|
|
|
func install() error {
|
|
fmt.Println("install")
|
|
if len(os.Args) < 4 {
|
|
log.Fatal("请输入要安装的服务类型和区号 install [center|node] 区号")
|
|
}
|
|
serverType := os.Args[2]
|
|
zone := os.Args[3]
|
|
if serverType != "center" && serverType != "node" {
|
|
log.Fatal("请输入正确的服务类型 center|node")
|
|
}
|
|
AppName := cfg.Section("app").Key("app_name").String()
|
|
// 生成服务名
|
|
serviceName := fmt.Sprintf("%s_%s_%s", AppName, serverType, zone)
|
|
// 判断文件夹是否存在
|
|
folderPath := fmt.Sprintf("%s/zone/%s", app_path, serviceName)
|
|
if _, err := os.Stat(folderPath); os.IsNotExist(err) {
|
|
err := os.MkdirAll(folderPath, os.ModePerm)
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
fmt.Printf("文件夹 %s 创建成功\n", folderPath)
|
|
} else {
|
|
fmt.Printf("文件夹 %s 已存在\n", folderPath)
|
|
}
|
|
|
|
// 生成配置文件
|
|
configPath := fmt.Sprintf("%s/server.json", folderPath)
|
|
file, err := os.Create(configPath)
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
defer file.Close()
|
|
zoneId, _ := strconv.Atoi(zone)
|
|
cf, confs := createConfigFile(zoneId, serverType)
|
|
file.WriteString(confs)
|
|
|
|
// 读取 SQL 文件内容并替换 %database% 字符串
|
|
sqlFilePath := fmt.Sprintf("%s/tool/Merge_Pet.sql", app_path)
|
|
sqlContent, err := os.ReadFile(sqlFilePath)
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
dbName := cf["DbName"].(string)
|
|
modifiedSQL := strings.ReplaceAll(string(sqlContent), "%database%", dbName)
|
|
|
|
// 将修改后的内容写入临时文件
|
|
tempSQLFile, err := os.CreateTemp("", "modified_*.sql")
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
// defer os.Remove(tempSQLFile.Name())
|
|
_, err = tempSQLFile.WriteString(modifiedSQL)
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
tempSQLFile.Close()
|
|
|
|
// 创建数据库
|
|
dbUser := cfg.Section("mysql").Key("mysql_user").String()
|
|
dbPassword := cfg.Section("mysql").Key("mysql_password").String()
|
|
dbHost := cfg.Section("mysql").Key("mysql_host").String()
|
|
dbPort := cfg.Section("mysql").Key("mysql_port").String()
|
|
fmt.Println("mysql", "-u"+dbUser, "-p"+dbPassword, "-h"+dbHost, "-P"+dbPort, "-e", fmt.Sprintf("CREATE DATABASE IF NOT EXISTS %s", dbName))
|
|
createDBCmd := exec.Command("mysql", "-u"+dbUser, "-p"+dbPassword, "-h"+dbHost, "-P"+dbPort, "-e", fmt.Sprintf("CREATE DATABASE IF NOT EXISTS %s", dbName))
|
|
createDBCmd.Stdout = os.Stdout
|
|
createDBCmd.Stderr = os.Stderr
|
|
|
|
err = createDBCmd.Run()
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
fmt.Println("mysql", "-u"+dbUser, "-p"+dbPassword, "-h"+dbHost, "-P"+dbPort, dbName, "-e", fmt.Sprintf("source %s", tempSQLFile.Name()))
|
|
// 执行修改后的 SQL 文件
|
|
cmd := exec.Command("mysql", "-u"+dbUser, "-p"+dbPassword, "-h"+dbHost, "-P"+dbPort, dbName, "-e", fmt.Sprintf("source %s", tempSQLFile.Name()))
|
|
cmd.Stdout = os.Stdout
|
|
cmd.Stderr = os.Stderr
|
|
|
|
err = cmd.Run()
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
cmd = exec.Command("mkdir", cf["LogPath"].(string))
|
|
cmd.Stdout = os.Stdout
|
|
cmd.Stderr = os.Stderr
|
|
err = cmd.Run()
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
fmt.Printf("SQL 文件 %s 执行成功\n", sqlFilePath)
|
|
fmt.Printf("配置文件 %s 创建成功\n", configPath)
|
|
return nil
|
|
}
|
|
|
|
func createConfigFile(Id int, Type string) (map[string]interface{}, string) {
|
|
conf := make(map[string]interface{})
|
|
conf["LogLevel"] = cfg.Section("log").Key("log_level").String()
|
|
|
|
TcpStartAddr, _ := cfg.Section("server").Key("tcp_addr").Int()
|
|
conf["TcpAddr"] = fmt.Sprintf(":%d", TcpStartAddr+Id)
|
|
|
|
WsStartAddr, _ := cfg.Section("server").Key("ws_addr").Int()
|
|
conf["WsAddr"] = fmt.Sprintf(":%d", WsStartAddr+Id)
|
|
|
|
ListenAddr, _ := cfg.Section("server").Key("listen_addr").Int()
|
|
|
|
conf["MySqlAddr"] = cfg.Section("mysql").Key("mysql_host").String()
|
|
conf["MySqlUsr"] = cfg.Section("mysql").Key("mysql_user").String()
|
|
conf["MySqlPwd"] = cfg.Section("mysql").Key("mysql_password").String()
|
|
conf["MySqlPort"] = cfg.Section("mysql").Key("mysql_port").String()
|
|
MaxConnNum, _ := cfg.Section("server").Key("max_conn").Int()
|
|
conf["MaxConnNum"] = MaxConnNum
|
|
app_name := cfg.Section("app").Key("app_name").String()
|
|
conf["LogPath"] = fmt.Sprintf("%s/zone/%s_%s_%d/log/", app_path, app_name, Type, Id)
|
|
conf["DbName"] = fmt.Sprintf("%s_%d", app_name, Id)
|
|
|
|
conf["TELOGDIR"] = cfg.Section("log").Key("te_log_path").String()
|
|
|
|
conf["GameName"] = app_name
|
|
AppId, _ := cfg.Section("app").Key("app_id").Int()
|
|
conf["AppID"] = AppId
|
|
|
|
conf["ServerType"] = Type
|
|
conf["ServerID"] = Id
|
|
|
|
conf["ServerOpenTime"] = "2028-01-01 00:00:00"
|
|
conf["ServerName"] = fmt.Sprintf("%s_%d", app_name, Id)
|
|
conf["ServerStatus"] = 1
|
|
CenterId, _ := cfg.Section("app").Key("center_id").Int()
|
|
conf["ServerCenter"] = CenterId
|
|
|
|
conf["RedisAddr"] = cfg.Section("redis").Key("redis_host").String()
|
|
conf["RedisPort"] = cfg.Section("redis").Key("redis_port").String()
|
|
conf["GameConfPath"] = app_path + "/config/"
|
|
conf["RemoteAddr"] = fmt.Sprintf("%s:%d", cfg.Section("server").Key("remote").String(), ListenAddr+Id) // 服务器地址
|
|
conf["ListenAddr"] = fmt.Sprintf(":%d", ListenAddr+Id)
|
|
conf["CenterAddr"] = fmt.Sprintf("%s:%s", cfg.Section("cluster").Key("center_host").String(), cfg.Section("cluster").Key("center_port").String()) // 服务器地址
|
|
b, _ := json.MarshalIndent(conf, "", " ")
|
|
return conf, string(b)
|
|
}
|
|
|
|
func start() error {
|
|
fmt.Println("start")
|
|
err := status()
|
|
if err == nil {
|
|
return err
|
|
}
|
|
NodeType := os.Args[2]
|
|
Zone := os.Args[3]
|
|
// 示例命令
|
|
app_name := cfg.Section("app").Key("app_name").String()
|
|
|
|
cmdName := app_path + "/main"
|
|
cmdArgs := []string{fmt.Sprintf("%s/zone/%s_%s_%s/server.json", app_path, app_name, NodeType, Zone)}
|
|
|
|
// 创建命令
|
|
cmd := exec.Command(cmdName, cmdArgs...)
|
|
cmd.SysProcAttr = &syscall.SysProcAttr{}
|
|
// 创建管道
|
|
stdin, err := cmd.StdinPipe()
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
cmd.Env = os.Environ()
|
|
// 打开输出文件
|
|
outfile, err := os.OpenFile(fmt.Sprintf("%s/zone/%s_%s_%s/output.log", app_path, app_name, NodeType, Zone), os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
defer outfile.Close()
|
|
|
|
// 重定向标准输出和标准错误到文件
|
|
cmd.Stdout = outfile
|
|
cmd.Stderr = outfile
|
|
err = cmd.Start()
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
|
|
// 打印进程号
|
|
fmt.Printf("进程 %d 已启动\n", cmd.Process.Pid)
|
|
|
|
// 发送命令到子进程
|
|
go func() {
|
|
time.Sleep(2 * time.Second) // 等待子进程启动
|
|
_, err := stdin.Write([]byte("your_command\n"))
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
stdin.Close()
|
|
}()
|
|
|
|
// 释放与子进程相关的资源
|
|
err = cmd.Process.Release()
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func stop() error {
|
|
fmt.Println("stop")
|
|
err := status()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
NodeType := os.Args[2]
|
|
Zone := os.Args[3]
|
|
// 示例命令
|
|
app_name := cfg.Section("app").Key("app_name").String()
|
|
processName := fmt.Sprintf("%s/zone/%s_%s_%s/server.json", app_path, app_name, NodeType, Zone)
|
|
// 示例进程名称
|
|
|
|
// 获取进程号
|
|
pid, err := getPidByArgs(processName)
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
|
|
// 查找进程
|
|
process, err := os.FindProcess(pid)
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
|
|
// 关闭进程
|
|
err = process.Kill()
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
|
|
// // 等待子进程退出
|
|
// _, err = process.Wait()
|
|
// if err != nil {
|
|
// log.Fatal(err)
|
|
// }
|
|
|
|
fmt.Printf("进程 %d 已关闭\n", pid)
|
|
return nil
|
|
}
|
|
|
|
// func getPidByName(name string) (int, error) {
|
|
// cmd := exec.Command("pgrep", name)
|
|
// output, err := cmd.Output()
|
|
// if err != nil {
|
|
// return 0, err
|
|
// }
|
|
|
|
// // 解析输出,获取第一个匹配的进程号
|
|
// outputStr := strings.TrimSpace(string(output))
|
|
// pidStr := strings.Split(outputStr, "\n")[0]
|
|
// pid, err := strconv.Atoi(pidStr)
|
|
// if err != nil {
|
|
// return 0, err
|
|
// }
|
|
|
|
// return pid, nil
|
|
// }
|
|
|
|
func getPidByArgs(args string) (int, error) {
|
|
cmd := exec.Command("pgrep", "-f", args)
|
|
output, err := cmd.Output()
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
|
|
// 解析输出,获取第一个匹配的进程号
|
|
outputStr := strings.TrimSpace(string(output))
|
|
pidStr := strings.Split(outputStr, "\n")[0]
|
|
pid, err := strconv.Atoi(pidStr)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
|
|
return pid, nil
|
|
}
|
|
|
|
func status() error {
|
|
NodeType := os.Args[2]
|
|
Zone := os.Args[3]
|
|
// 示例命令
|
|
app_name := cfg.Section("app").Key("app_name").String()
|
|
processName := fmt.Sprintf("%s/zone/%s_%s_%s/server.json", app_path, app_name, NodeType, Zone)
|
|
// 示例进程名称
|
|
|
|
// 获取进程号
|
|
pid, err := getPidByArgs(processName)
|
|
if err != nil {
|
|
return fmt.Errorf("进程 %s_%s 未启动", NodeType, Zone)
|
|
}
|
|
|
|
// 查找进程
|
|
_, err = os.FindProcess(pid)
|
|
if err != nil {
|
|
return fmt.Errorf("进程 %s_%s 未启动", NodeType, Zone)
|
|
}
|
|
|
|
fmt.Printf("节点 %s_%s 启动中, 进程号 %d\n", NodeType, Zone, pid)
|
|
return nil
|
|
}
|
|
|
|
func restart() error {
|
|
fmt.Println("restart")
|
|
err := stop()
|
|
if err != nil {
|
|
fmt.Println(err)
|
|
}
|
|
for status() == nil {
|
|
time.Sleep(1 * time.Second)
|
|
}
|
|
err = start()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func restartAll() error {
|
|
fmt.Println("restartAll")
|
|
err := stop()
|
|
if err != nil {
|
|
fmt.Println(err)
|
|
}
|
|
for status() == nil {
|
|
time.Sleep(1 * time.Second)
|
|
}
|
|
err = start()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|