/* Sog 游戏基础库 2016 by zouwei */ using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; namespace Sog { public delegate void OnClusterMessageHandler(uint remoteAppID, MessageData message); public enum ServerStopStage { none = 0, prepare = 1, stopping = 2, } public class ServerApp { private bool m_bRunning; private Cluster m_cluster; private int m_iTickCountForSleep; private int MaxTickCountNeedSleep = 1; private int m_noTickCount; public int ProcessMsgCountOnce = 100; private long m_lastExceptionTime; private int m_exceptionCount; public long TotalTickCount { get; private set; } //服务器总共tick次数 public long ServerStartTime; //服务器开始运行的时间 //服务器id,也叫appid public uint ServerID { get; private set; } //字符串格式的serverid public string StrServerID { get; private set; } public AppParam AppParam { get; private set; } public AppTime Time { get; } public string ClusterName { get { return m_cluster.ClusterName; } } /// /// 随机数 /// public Random Rand { get; } /// /// /// public AppAlerter Alerter { get; } public ServerStopStage CurrStopStage { get; private set; } public delegate void OnInitHandler(); public delegate void OnStopHandler(); public delegate void OnReloadHandler(); public delegate void OnTickHandler(long nowMs); public OnInitHandler OnInit; public OnStopHandler OnStop; public OnReloadHandler OnReload; public OnTickHandler OnTick; public OnClusterMessageHandler OnClusterMessage; public ServerApp(AppParam appParam) { AppParam = appParam; Time = new AppTime(); Time.UpdateTime(); Rand = new Random(); ServerID = AppParam.ServerID; StrServerID = ServerIDUtils.IDToString(ServerID); m_bRunning = false; ServerStartTime = DateTimeOffset.Now.ToUnixTimeMilliseconds()/1000; JsonConfig.InitLitJson(); m_cluster = new Cluster(this, ServerID); Alerter = new AppAlerter(this); CurrStopStage = ServerStopStage.none; if(OSUtils.IsWindows()) { MaxTickCountNeedSleep = 10; } //如果有cluster配置的话 if (AppParam.ClusterFileName != null) { m_cluster.InitAppByConfig(AppParam.ClusterFileName); if (AppParam.ConfigFileName == null) { AppParam.ConfigFileName = m_cluster.GetAppConfigFile(); AppParam.LoadServerConfig(); } } } public Cluster GetCluster() { return m_cluster; } public void Run() { //windows的sleep显然精度更低 //要符合要求,至少设置成10,但是windows反正不跑生产环境,这里设置成1就可以了,无所谓精度 //linux usleep的精度很高,先设置成1试试,最多2够了 //m_iIdleCountForSleep = OSUtils.IsWindows() ? 2 : 1; //m_iIdleSleepMS = 1; m_bRunning = true; m_cluster.Start(); while (m_bRunning) { try { TotalTickCount++; Time.UpdateTime(); //GCTest.Instance.TestMemoryNew(); long nowMs = Time.GetTime(); int iRecvMsgCount = m_cluster.Update(OnClusterMessage, ProcessMsgCountOnce, nowMs); if(iRecvMsgCount >= ProcessMsgCountOnce) { m_noTickCount++; //最多处理10次消息,如果消息还没处理完成,强制tick一次 if(m_noTickCount > 5) { m_noTickCount = 0; } } else { m_noTickCount = 0; } if (m_noTickCount == 0) { if (OnTick != null) { m_iTickCountForSleep++; GCMonitor.Instance.ScriptOnTick(); OnTick(nowMs); } // windows下sleep一下要10ms,linux虚拟机也快不到哪里去,所以不能sleep太多 if (m_iTickCountForSleep >= MaxTickCountNeedSleep) { m_iTickCountForSleep = 0; System.Threading.Thread.Sleep(1); } } GCMonitor.Instance.Tick(); } catch (Exception ex) { m_exceptionCount++; long nowMs = Time.GetTime(); // 1秒只能抛出有限个异常,太多的直接忽略,免得影响服务器效率 if (nowMs - m_lastExceptionTime > 1000) { m_lastExceptionTime = nowMs; m_exceptionCount = 0; } if (m_exceptionCount <= 1) { TraceLog.Exception(ex); } //windows下发现逻辑错误直接退出程序 if (OSUtils.IsWindows()) { //等待写日志 System.Threading.Thread.Sleep(1000); break; } else { Alerter.AlertException(ex); } } } m_cluster.Close(); } public void RunInBackground() { System.Threading.Thread runTask = new System.Threading.Thread(this.Run); runTask.Start(); } public long GetTimeSecond() { return Time.GetTimeSecond(); } public bool IsStopping => CurrStopStage != ServerStopStage.none; public bool IsRunning() { return m_bRunning; } public void Exit() { TraceLog.Debug("ServerApp.Exit be call, exit server!"); m_bRunning = false; } public void PrepareStop() { if (CurrStopStage == ServerStopStage.none) { CurrStopStage = ServerStopStage.prepare; } } public void StopServer() { CurrStopStage = ServerStopStage.stopping; if (OnStop != null) { OnStop(); } } //逻辑服务器停服成功后调用,服务器将会退出 public void SetStopSuccess() { m_bRunning = false; } } }