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.
 
 
 
 
 
 

269 lines
7.3 KiB

/*
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; }
}
/// <summary>
/// 随机数
/// </summary>
public Random Rand { get; }
/// <summary>
///
/// </summary>
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;
}
}
}