You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
439 lines
14 KiB
439 lines
14 KiB
using System;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Reflection;
|
|
using System.Security.Cryptography;
|
|
using System.Threading.Tasks;
|
|
using Sog;
|
|
using Google.Protobuf.WellKnownTypes;
|
|
|
|
namespace SMCenter
|
|
{
|
|
|
|
|
|
// 通用命令处理
|
|
public class CommCmdProc : BaseCmdProc
|
|
{
|
|
private long LastCheckTimeoutSecond;
|
|
|
|
public override void ClearData()
|
|
{
|
|
m_nowCmd = null;
|
|
LastCheckTimeoutSecond = 0;
|
|
}
|
|
|
|
// cmdParams[0] is serverId
|
|
public override int DoCmd(out string msg)
|
|
{
|
|
string lowerCMD = m_nowCmd.CMD;
|
|
|
|
if (m_nowCmd.CmdParams.Count < 1 || string.IsNullOrEmpty(m_nowCmd.CmdParams[0]))
|
|
{
|
|
msg = "invalid cmd param";
|
|
return -1;
|
|
}
|
|
|
|
if(lowerCMD == "list")
|
|
{
|
|
SMProcAppMgr.Instance.GetAllMatchingProc("*.*.*", m_nowCmd.m_procs);
|
|
if(m_nowCmd.CmdParams[0] != "*")
|
|
{
|
|
m_nowCmd.m_procs = m_nowCmd.m_procs.Where(p => p.Value.SMApp.HostName == m_nowCmd.CmdParams[0]).ToDictionary(p => p.Key, p => p.Value);
|
|
m_nowCmd.m_procs = m_nowCmd.m_procs.OrderBy(o => o.Value.SMApp.HostName).ToDictionary(p => p.Key, o => o.Value);
|
|
}
|
|
|
|
if(m_nowCmd.m_procs.Count == 0)
|
|
{
|
|
msg = "invalid cmd param or HostName is not exist";
|
|
return -1;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (CmdUtils.CheckValidServerId(m_nowCmd.CmdParams[0], out string fixSvrId) == false)
|
|
{
|
|
msg = "invalid server id";
|
|
return -1;
|
|
}
|
|
|
|
if (fixSvrId != null)
|
|
{
|
|
m_nowCmd.CmdParams[0] = fixSvrId;
|
|
}
|
|
|
|
SMProcAppMgr.Instance.GetAllMatchingProc(m_nowCmd.CmdParams[0], m_nowCmd.m_procs);
|
|
}
|
|
|
|
TraceLog.Debug("CommCmdProc.DoCmd {0} match proc num {1}", m_nowCmd.CMD, m_nowCmd.m_procs.Count);
|
|
|
|
//需要排序的命令
|
|
if (lowerCMD == "start")
|
|
{
|
|
//根据sequence排序
|
|
m_nowCmd.m_procs = m_nowCmd.m_procs.OrderBy(p => p.Value.SMApp.HostName).ThenBy(o => o.Value.SMApp.Sequence).ToDictionary(p => p.Key, o => o.Value);
|
|
}
|
|
else if (lowerCMD == "stop")
|
|
{
|
|
//根据sequence排序
|
|
m_nowCmd.m_procs = m_nowCmd.m_procs.OrderBy(p => p.Value.SMApp.HostName).ThenByDescending(o => o.Value.SMApp.Sequence).ToDictionary(p => p.Key, o => o.Value);
|
|
}
|
|
|
|
if(m_nowCmd.m_procs.Count == 0)
|
|
{
|
|
msg ="not exist ServerID: " + m_nowCmd.CmdParams[0];
|
|
return -1;
|
|
}
|
|
|
|
var regHost = SMCenterNet.Instance.GetAllAgent();
|
|
foreach (var procInfo in m_nowCmd.m_procs)
|
|
{
|
|
if(regHost.Find( p => p.HostName == procInfo.Value.SMApp.HostName) == null)
|
|
{
|
|
procInfo.Value.SendCmdToHostTime = 1;
|
|
procInfo.Value.AckResultCode = ResResultCode.Fail;
|
|
procInfo.Value.AckMessage = procInfo.Value.SMApp.HostName + " Agent Not Running";
|
|
procInfo.Value.AckAddInfo = "0 KB";
|
|
}
|
|
}
|
|
|
|
if (IsAllProcCmdAcked())
|
|
{
|
|
AckConsoleCmdResult();
|
|
}
|
|
|
|
msg = m_nowCmd.ConsoleInput + " begin ...";
|
|
TraceLog.Trace("CommCmdProc.DoCmd {0}", msg);
|
|
return 0;
|
|
}
|
|
|
|
public override int UpdateCmd(long tMs)
|
|
{
|
|
if (m_nowCmd.m_procs.Count == 0)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
if (IsAllProcCmdAcked())
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
CheckProcTimeout(tMs);
|
|
|
|
if (m_nowCmd.CMD =="start" || m_nowCmd.CMD == "stop")
|
|
{
|
|
UpdateSequenceCmd(tMs);
|
|
}
|
|
else
|
|
{
|
|
//send one cmd per tick
|
|
foreach (var cmdInfo in m_nowCmd.m_procs.Values)
|
|
{
|
|
if (cmdInfo.SendCmdToHostTime == 0)
|
|
{
|
|
SendCmdToHost(cmdInfo);
|
|
|
|
cmdInfo.SendCmdToHostTime = tMs / 1000;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
private void CheckProcTimeout(long tMs)
|
|
{
|
|
long nowSecond = tMs / 1000;
|
|
|
|
if(nowSecond - LastCheckTimeoutSecond < 2)
|
|
{
|
|
return;
|
|
}
|
|
|
|
LastCheckTimeoutSecond = nowSecond;
|
|
|
|
foreach (var cmdInfo in m_nowCmd.m_procs.Values)
|
|
{
|
|
if (cmdInfo.SendCmdToHostTime > 0 && cmdInfo.AckResultCode == ResResultCode.NoRes)
|
|
{
|
|
int timeoutSecond = 15;
|
|
if(m_nowCmd.CMD == "stop")
|
|
{
|
|
timeoutSecond = 60;
|
|
}
|
|
|
|
if (nowSecond - cmdInfo.SendCmdToHostTime > timeoutSecond)
|
|
{
|
|
TraceLog.Error("CommCmdProc.CheckProcTimeout cmd {0} serverid {1} timeout"
|
|
, m_nowCmd.CMD, cmdInfo.SMApp.ServerIDStr);
|
|
|
|
cmdInfo.AckResultCode = ResResultCode.TimeOut;
|
|
|
|
if (IsAllProcCmdAcked())
|
|
{
|
|
AckConsoleCmdResult();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private int UpdateSequenceCmd(long tMs)
|
|
{
|
|
//根据Sequence一次发送一批命令
|
|
int thisSendSequence = -1;
|
|
bool needExit = false;
|
|
CmdProcInfo errorCmdProc = null;
|
|
|
|
foreach (CmdProcInfo cmdProc in m_nowCmd.m_procs.Values)
|
|
{
|
|
// 停服时, 如果是*.*.*这种形式的输入, 会跳过某些appType, 比如Version和GateVersion
|
|
if (m_nowCmd.CMD == "stop")
|
|
{
|
|
string appType = m_nowCmd.CmdParams[0].Split('.')[1];
|
|
if (appType == "*" && SMCenterUtils.Config.excludeAppType.Contains(cmdProc.SMApp.AppType))
|
|
{
|
|
cmdProc.SendCmdToHostTime = tMs / 1000;
|
|
cmdProc.AckResultCode = ResResultCode.Success;
|
|
cmdProc.AckMessage = " Cannot stop *.*.*, Run [stop *.app.*] or Run [list *] check server state";
|
|
continue;
|
|
}
|
|
}
|
|
|
|
//前面优先级的进程命令未成功完成,直接退出循环,超时的情况也不能继续往下走
|
|
if (cmdProc.SendCmdToHostTime > 0 && cmdProc.AckResultCode != ResResultCode.Success)
|
|
{
|
|
if (cmdProc.AckResultCode == ResResultCode.Exception
|
|
|| cmdProc.AckResultCode == ResResultCode.Fail
|
|
|| cmdProc.AckResultCode == ResResultCode.TimeOut)
|
|
{
|
|
errorCmdProc = cmdProc;
|
|
needExit = true;
|
|
}
|
|
break;
|
|
}
|
|
|
|
//发现没有发送CMD的进程,取出Sequence记录,相同Sequence的进程可以一起发送命令
|
|
if (cmdProc.SendCmdToHostTime == 0)
|
|
{
|
|
if(thisSendSequence == -1)
|
|
{
|
|
thisSendSequence = cmdProc.SMApp.Sequence;
|
|
}
|
|
else if (thisSendSequence != cmdProc.SMApp.Sequence)
|
|
{
|
|
break;
|
|
}
|
|
|
|
SendCmdToHost(cmdProc);
|
|
cmdProc.SendCmdToHostTime = tMs / 1000;
|
|
}
|
|
}
|
|
|
|
if(needExit && errorCmdProc != null)
|
|
{
|
|
// 退出时把本次执行cmd的所有proc.result都设置成出错的proc的错误码
|
|
foreach (CmdProcInfo cmdProc in m_nowCmd.m_procs.Values)
|
|
{
|
|
cmdProc.AckResultCode = errorCmdProc.AckResultCode;
|
|
}
|
|
|
|
AckConsoleCmdResult();
|
|
return -1;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
|
|
public override void OnAgentDoCommandRes(ClientInfo client, SMAgentDoCommandRes res)
|
|
{
|
|
if (m_nowCmd.SeqNum != res.SeqNum)
|
|
{
|
|
TraceLog.Error("CommCmdProc.OnAgentDoCommandRes CMD {0} serverId {1}, seqNum not same, skip, m_nowCmd.SeqNum {2}"
|
|
, res.Command, res.ServerId, m_nowCmd.SeqNum);
|
|
return;
|
|
}
|
|
|
|
if(m_nowCmd.m_procs.ContainsKey(res.ServerId) == false)
|
|
{
|
|
TraceLog.Error("CommCmdProc.OnAgentDoCommandRes CMD {0} serverId {1} not in m_procs, skip"
|
|
, res.Command, res.ServerId);
|
|
return;
|
|
}
|
|
|
|
CmdProcInfo proc = m_nowCmd.m_procs[res.ServerId];
|
|
proc.AckResultCode = res.ResultCode;
|
|
proc.AckMessage = res.Result;
|
|
proc.AckAddInfo = res.AddInfo;
|
|
|
|
TraceLog.Trace("+++proc+++ " + "\t\t\t" + proc.SMApp.Name+ "\t\t\t\t" + proc.SMApp.ServerIDStr + "\t\t" + res.Command + "\t\t" + res.Result + "\t\t" + res.AddInfo);
|
|
|
|
if(IsAllProcCmdAcked())
|
|
{
|
|
AckConsoleCmdResult();
|
|
}
|
|
|
|
TraceLog.Trace("CommCmdProc.OnAgentDoCommandRes CMD {0} serverId {1}, result {2}"
|
|
, res.Command, res.ServerId, res.Result);
|
|
}
|
|
|
|
|
|
private bool IsAllProcCmdAcked()
|
|
{
|
|
foreach(var proc in m_nowCmd.m_procs)
|
|
{
|
|
if(proc.Value.AckResultCode == ResResultCode.NoRes)
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
private void AckConsoleCmdResult()
|
|
{
|
|
SMConsoleCommandRes res = new SMConsoleCommandRes();
|
|
res.Command = m_nowCmd.ConsoleInput;
|
|
res.Message = "";
|
|
|
|
List<string> hostName = new List<string>();
|
|
foreach (var procInfo in m_nowCmd.m_procs)
|
|
{
|
|
string procResult = "";
|
|
|
|
if (! hostName.Contains(procInfo.Value.SMApp.HostName))
|
|
{
|
|
procResult += string.Format("\n--------------- HostName:{0} ---------------", procInfo.Value.SMApp.HostName);
|
|
hostName.Add(procInfo.Value.SMApp.HostName);
|
|
}
|
|
|
|
procResult += "\n " + procInfo.Value.SMApp.ServerIDStr + "\t";
|
|
|
|
switch(procInfo.Value.AckResultCode)
|
|
{
|
|
case ResResultCode.NoRes:
|
|
case ResResultCode.TimeOut:
|
|
procResult += "Timeout";
|
|
break;
|
|
case ResResultCode.Success:
|
|
procResult += "Success";
|
|
break;
|
|
case ResResultCode.Fail:
|
|
procResult += "Fail";
|
|
break;
|
|
case ResResultCode.Exception:
|
|
procResult += "Exception";
|
|
break;
|
|
case ResResultCode.Running:
|
|
procResult += "Running";
|
|
break;
|
|
case ResResultCode.NotRunning:
|
|
procResult += "Not Running";
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
//不会空的情况下定义为错误信息
|
|
if(string.IsNullOrEmpty(procInfo.Value.AckMessage) == false)
|
|
{
|
|
procResult += " (INFO:" + procInfo.Value.AckMessage + ")";
|
|
}
|
|
|
|
//这个携带一些其他数据信息(目前只有内存消息)
|
|
if (string.IsNullOrEmpty(procInfo.Value.AckAddInfo))
|
|
{
|
|
procInfo.Value.AckAddInfo = "0 KB";
|
|
}
|
|
procResult += string.Format(" \tMemoryUsage:{0,-30}", procInfo.Value.AckAddInfo);
|
|
|
|
procResult += string.Format("\t\"{0}@{1}\"",procInfo.Value.SMApp.Name, procInfo.Value.SMApp.HostName);
|
|
procResult += string.Format("\t{0}", procInfo.Value.SMApp.CfgPath);
|
|
procResult += string.Format("\t{0}", procInfo.Value.SMApp.WorkPath);
|
|
|
|
res.Message += procResult;
|
|
}
|
|
|
|
SMCenterNet.Instance.SendMsg(m_nowCmd.SessionId, SMMsgID.ConsoleCommandRes, res, m_nowCmd.HttpId);
|
|
}
|
|
|
|
|
|
private void SendCmdToHost(CmdProcInfo cmdInfo)
|
|
{
|
|
string hostname = cmdInfo.SMApp.HostName;
|
|
|
|
ClientInfo client = SMCenterNet.Instance.GetClientInfoByName(hostname);
|
|
if(client == null)
|
|
{
|
|
TraceLog.Error("CommCmdProc.SendCmdToHost host {0} agent not register!", hostname);
|
|
return;
|
|
}
|
|
|
|
SMAgentDoCommandReq req = new SMAgentDoCommandReq();
|
|
req.Command = m_nowCmd.CMD;
|
|
req.SeqNum = m_nowCmd.SeqNum;
|
|
req.ServerId = cmdInfo.SMApp.AppId;
|
|
req.WorkPath = cmdInfo.SMApp.WorkPath;
|
|
req.StopTimeout = cmdInfo.SMApp.StopTimeout;
|
|
req.ExeFileName = cmdInfo.SMApp.ExeFileName;
|
|
//构造命令行参数
|
|
req.CmdArgs = GetCmdArgs(cmdInfo, m_nowCmd);
|
|
|
|
client.SendMsg(req, SMMsgID.AgentDoCommandReq);
|
|
}
|
|
|
|
|
|
public string GetExeFileName()
|
|
{
|
|
string exename = "SogLoader";
|
|
if (OSUtils.IsWindows())
|
|
{
|
|
exename += ".exe";
|
|
}
|
|
|
|
return exename;
|
|
}
|
|
|
|
|
|
private string GetCmdArgs(CmdProcInfo cmdProcInfo, CmdInfo cmdInfo)
|
|
{
|
|
//统一是这个格式,有些命令agent会特殊处理,比如check
|
|
//--name=Game这个参数纯粹是为了肉眼好看,只是起到注释作用,ps看进程的时候进程太多,只看id眼花
|
|
string args = string.Format("--id={0} --cluster={1}/cluster.json --name={2} {3}"
|
|
, cmdProcInfo.SMApp.ServerIDStr,cmdProcInfo.SMApp.CfgPath, cmdProcInfo.SMApp.Name, cmdInfo.CMD);
|
|
|
|
//gm指令的话需要带上参数
|
|
//这里的gm指令只能输入一些简单字符,比如中文没法输入,代码不好写,算了,复杂的gm指令通过经营分析系统去做,SMS就不管了
|
|
//gmcmd 1.200.1 AddChip 50000 -t10001
|
|
//类似这种简单的指令没有问题
|
|
//包含特殊字符,或者中文,其他非字符的gm指令非得用SMS输入也不是不可以
|
|
//gm指令本身支持base64转码
|
|
//gmcmd 1.200.1 AddChip 500000000 -t 1000001
|
|
//用base64编码替换AddChip后面的参数
|
|
//gmcmd 1.200.1 AddChip -bNTAwMDAwMDAwIC10IDEwMDAwMDE=
|
|
//gmcmd 1.200.1 AddChip -b NTAwMDAwMDAwIC10IDEwMDAwMDE=
|
|
//以上两种都可以
|
|
if (cmdInfo.CMD == "gmcmd" && cmdInfo.CmdParams.Count > 1)
|
|
{
|
|
//第0个是服务器id
|
|
for(int i= 1; i < cmdInfo.CmdParams.Count; i++)
|
|
{
|
|
args += cmdInfo.CmdParams[i];
|
|
if(i != cmdInfo.CmdParams.Count -1)
|
|
{
|
|
args += " ";
|
|
}
|
|
}
|
|
}
|
|
|
|
return args;
|
|
}
|
|
}
|
|
|
|
|
|
}
|
|
|